<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/controls/textfield/textfield.h"

#include "app/gfx/insets.h"
#if defined(OS_WIN)
#include "app/win_util.h"
#endif
#include "base/string_util.h"
#include "views/controls/textfield/native_textfield_wrapper.h"
#include "views/widget/widget.h"

namespace views {

// static
const char Textfield::kViewClassName[] = "views/Textfield";

/////////////////////////////////////////////////////////////////////////////
// Textfield

Textfield::Textfield()
    : native_wrapper_(NULL),
      controller_(NULL),
      style_(STYLE_DEFAULT),
      read_only_(false),
      default_width_in_chars_(0),
      draw_border_(true),
      background_color_(SK_ColorWHITE),
      use_default_background_color_(true),
      num_lines_(1),
      initialized_(false) {
  SetFocusable(true);
}

Textfield::Textfield(StyleFlags style)
    : native_wrapper_(NULL),
      controller_(NULL),
      style_(style),
      read_only_(false),
      default_width_in_chars_(0),
      draw_border_(true),
      background_color_(SK_ColorWHITE),
      use_default_background_color_(true),
      num_lines_(1),
      initialized_(false) {
  SetFocusable(true);
}

Textfield::~Textfield() {
  if (native_wrapper_)
    delete native_wrapper_;
}

void Textfield::SetController(Controller* controller) {
  controller_ = controller;
}

Textfield::Controller* Textfield::GetController() const {
  return controller_;
}

void Textfield::SetReadOnly(bool read_only) {
  read_only_ = read_only;
  if (native_wrapper_) {
    native_wrapper_->UpdateReadOnly();
    native_wrapper_->UpdateBackgroundColor();
  }
}

bool Textfield::IsPassword() const {
  return style_ & STYLE_PASSWORD;
}

bool Textfield::IsMultiLine() const {
  return !!(style_ & STYLE_MULTILINE);
}

void Textfield::SetText(const std::wstring& text) {
  text_ = text;
  if (native_wrapper_)
    native_wrapper_->UpdateText();
}

void Textfield::AppendText(const std::wstring& text) {
  text_ += text;
  if (native_wrapper_)
    native_wrapper_->AppendText(text);
}

void Textfield::SelectAll() {
  if (native_wrapper_)
    native_wrapper_->SelectAll();
}

void Textfield::ClearSelection() const {
  if (native_wrapper_)
    native_wrapper_->ClearSelection();
}

void Textfield::SetBackgroundColor(SkColor color) {
  background_color_ = color;
  use_default_background_color_ = false;
  if (native_wrapper_)
    native_wrapper_->UpdateBackgroundColor();
}

void Textfield::UseDefaultBackgroundColor() {
  use_default_background_color_ = true;
  if (native_wrapper_)
    native_wrapper_->UpdateBackgroundColor();
}

void Textfield::SetFont(const gfx::Font& font) {
  font_ = font;
  if (native_wrapper_)
    native_wrapper_->UpdateFont();
}

void Textfield::SetHorizontalMargins(int left, int right) {
  if (native_wrapper_)
    native_wrapper_->SetHorizontalMargins(left, right);
}

void Textfield::SetHeightInLines(int num_lines) {
  DCHECK(IsMultiLine());
  num_lines_ = num_lines;
}

void Textfield::RemoveBorder() {
  if (!draw_border_)
    return;

  draw_border_ = false;
  if (native_wrapper_)
    native_wrapper_->UpdateBorder();
}

void Textfield::CalculateInsets(gfx::Insets* insets) {
  DCHECK(insets);

  if (!draw_border_)
    return;

  // NOTE: One would think GetThemeMargins would return the insets we should
  // use, but it doesn't. The margins returned by GetThemeMargins are always
  // 0.

  // This appears to be the insets used by Windows.
  insets->Set(3, 3, 3, 3);
}

void Textfield::SyncText() {
  if (native_wrapper_)
    text_ = native_wrapper_->GetText();
}

// static
bool Textfield::IsKeystrokeEnter(const Keystroke& key) {
#if defined(OS_WIN)
  return key.key == VK_RETURN;
#else
  // TODO(port): figure out VK_constants
  NOTIMPLEMENTED();
  return false;
#endif
}

// static
bool Textfield::IsKeystrokeEscape(const Keystroke& key) {
#if defined(OS_WIN)
  return key.key == VK_ESCAPE;
#else
  // TODO(port): figure out VK_constants
  NOTIMPLEMENTED();
  return false;
#endif
}

////////////////////////////////////////////////////////////////////////////////
// Textfield, View overrides:

void Textfield::Layout() {
  if (native_wrapper_) {
    native_wrapper_->GetView()->SetBounds(GetLocalBounds(true));
    native_wrapper_->GetView()->Layout();
  }
}

gfx::Size Textfield::GetPreferredSize() {
  gfx::Insets insets;
  CalculateInsets(&insets);
  return gfx::Size(font_.GetExpectedTextWidth(default_width_in_chars_) +
                       insets.width(),
                   num_lines_ * font_.height() + insets.height());
}

bool Textfield::IsFocusable() const {
  return IsEnabled() && !read_only_;
}

void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
  SelectAll();
}

bool Textfield::SkipDefaultKeyEventProcessing(const KeyEvent& e) {
#if defined(OS_WIN)
  // TODO(hamaji): Figure out which keyboard combinations we need to add here,
  //               similar to LocationBarView::SkipDefaultKeyEventProcessing.
  const int c = e.GetCharacter();
  if (c == VK_BACK)
    return true;  // We'll handle BackSpace ourselves.

  // We don't translate accelerators for ALT + NumPad digit, they are used for
  // entering special characters.  We do translate alt-home.
  if (e.IsAltDown() && (c != VK_HOME) &&
      win_util::IsNumPadDigit(c, e.IsExtendedKey()))
    return true;
#endif
  return false;
}

void Textfield::SetEnabled(bool enabled) {
  View::SetEnabled(enabled);
  if (native_wrapper_)
    native_wrapper_->UpdateEnabled();
}

void Textfield::Focus() {
  if (native_wrapper_) {
    // Forward the focus to the wrapper if it exists.
    native_wrapper_->SetFocus();
  } else {
    // If there is no wrapper, cause the RootView to be focused so that we still
    // get keyboard messages.
    View::Focus();
  }
}

void Textfield::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
  if (is_add && !native_wrapper_ && GetWidget() && !initialized_) {
    initialized_ = true;
    native_wrapper_ = NativeTextfieldWrapper::CreateWrapper(this);
    //AddChildView(native_wrapper_->GetView());
    // TODO(beng): Move this initialization to NativeTextfieldWin once it
    //             subclasses NativeControlWin.
    native_wrapper_->UpdateText();
    native_wrapper_->UpdateBackgroundColor();
    native_wrapper_->UpdateReadOnly();
    native_wrapper_->UpdateFont();
    native_wrapper_->UpdateEnabled();
    native_wrapper_->UpdateBorder();
  }
}

std::string Textfield::GetClassName() const {
  return kViewClassName;
}

}  // namespace views
<commit_msg>Lands http://codereview.chromium.org/131064 for tedoc2000.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/controls/textfield/textfield.h"

#include "app/gfx/insets.h"
#if defined(OS_WIN)
#include "app/win_util.h"
#endif
#include "base/string_util.h"
#include "views/controls/textfield/native_textfield_wrapper.h"
#include "views/widget/widget.h"

namespace views {

// static
const char Textfield::kViewClassName[] = "views/Textfield";

/////////////////////////////////////////////////////////////////////////////
// Textfield

Textfield::Textfield()
    : native_wrapper_(NULL),
      controller_(NULL),
      style_(STYLE_DEFAULT),
      read_only_(false),
      default_width_in_chars_(0),
      draw_border_(true),
      background_color_(SK_ColorWHITE),
      use_default_background_color_(true),
      num_lines_(1),
      initialized_(false) {
  SetFocusable(true);
}

Textfield::Textfield(StyleFlags style)
    : native_wrapper_(NULL),
      controller_(NULL),
      style_(style),
      read_only_(false),
      default_width_in_chars_(0),
      draw_border_(true),
      background_color_(SK_ColorWHITE),
      use_default_background_color_(true),
      num_lines_(1),
      initialized_(false) {
  SetFocusable(true);
}

Textfield::~Textfield() {
  if (native_wrapper_)
    delete native_wrapper_;
}

void Textfield::SetController(Controller* controller) {
  controller_ = controller;
}

Textfield::Controller* Textfield::GetController() const {
  return controller_;
}

void Textfield::SetReadOnly(bool read_only) {
  read_only_ = read_only;
  if (native_wrapper_) {
    native_wrapper_->UpdateReadOnly();
    native_wrapper_->UpdateBackgroundColor();
  }
}

bool Textfield::IsPassword() const {
  return style_ & STYLE_PASSWORD;
}

bool Textfield::IsMultiLine() const {
  return !!(style_ & STYLE_MULTILINE);
}

void Textfield::SetText(const std::wstring& text) {
  text_ = text;
  if (native_wrapper_)
    native_wrapper_->UpdateText();
}

void Textfield::AppendText(const std::wstring& text) {
  text_ += text;
  if (native_wrapper_)
    native_wrapper_->AppendText(text);
}

void Textfield::SelectAll() {
  if (native_wrapper_)
    native_wrapper_->SelectAll();
}

void Textfield::ClearSelection() const {
  if (native_wrapper_)
    native_wrapper_->ClearSelection();
}

void Textfield::SetBackgroundColor(SkColor color) {
  background_color_ = color;
  use_default_background_color_ = false;
  if (native_wrapper_)
    native_wrapper_->UpdateBackgroundColor();
}

void Textfield::UseDefaultBackgroundColor() {
  use_default_background_color_ = true;
  if (native_wrapper_)
    native_wrapper_->UpdateBackgroundColor();
}

void Textfield::SetFont(const gfx::Font& font) {
  font_ = font;
  if (native_wrapper_)
    native_wrapper_->UpdateFont();
}

void Textfield::SetHorizontalMargins(int left, int right) {
  if (native_wrapper_)
    native_wrapper_->SetHorizontalMargins(left, right);
}

void Textfield::SetHeightInLines(int num_lines) {
  DCHECK(IsMultiLine());
  num_lines_ = num_lines;
}

void Textfield::RemoveBorder() {
  if (!draw_border_)
    return;

  draw_border_ = false;
  if (native_wrapper_)
    native_wrapper_->UpdateBorder();
}

void Textfield::CalculateInsets(gfx::Insets* insets) {
  DCHECK(insets);

  if (!draw_border_)
    return;

  // NOTE: One would think GetThemeMargins would return the insets we should
  // use, but it doesn't. The margins returned by GetThemeMargins are always
  // 0.

  // This appears to be the insets used by Windows.
  insets->Set(3, 3, 3, 3);
}

void Textfield::SyncText() {
  if (native_wrapper_)
    text_ = native_wrapper_->GetText();
}

// static
bool Textfield::IsKeystrokeEnter(const Keystroke& key) {
#if defined(OS_WIN)
  return key.key == VK_RETURN;
#else
  // TODO(port): figure out VK_constants
  NOTIMPLEMENTED();
  return false;
#endif
}

// static
bool Textfield::IsKeystrokeEscape(const Keystroke& key) {
#if defined(OS_WIN)
  return key.key == VK_ESCAPE;
#else
  // TODO(port): figure out VK_constants
  NOTIMPLEMENTED();
  return false;
#endif
}

////////////////////////////////////////////////////////////////////////////////
// Textfield, View overrides:

void Textfield::Layout() {
  if (native_wrapper_) {
    native_wrapper_->GetView()->SetBounds(GetLocalBounds(true));
    native_wrapper_->GetView()->Layout();
  }
}

gfx::Size Textfield::GetPreferredSize() {
  gfx::Insets insets;
  CalculateInsets(&insets);
  return gfx::Size(font_.GetExpectedTextWidth(default_width_in_chars_) +
                       insets.width(),
                   num_lines_ * font_.height() + insets.height());
}

bool Textfield::IsFocusable() const {
  return IsEnabled() && !read_only_;
}

void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) {
  SelectAll();
}

bool Textfield::SkipDefaultKeyEventProcessing(const KeyEvent& e) {
#if defined(OS_WIN)
  // TODO(hamaji): Figure out which keyboard combinations we need to add here,
  //               similar to LocationBarView::SkipDefaultKeyEventProcessing.
  const int c = e.GetCharacter();
  if (c == VK_BACK)
    return true;  // We'll handle BackSpace ourselves.

  // We don't translate accelerators for ALT + NumPad digit, they are used for
  // entering special characters.  We do translate alt-home.
  if (e.IsAltDown() && (c != VK_HOME) &&
      win_util::IsNumPadDigit(c, e.IsExtendedKey()))
    return true;
#endif
  return false;
}

void Textfield::SetEnabled(bool enabled) {
  View::SetEnabled(enabled);
  if (native_wrapper_)
    native_wrapper_->UpdateEnabled();
}

void Textfield::Focus() {
  if (native_wrapper_) {
    // Forward the focus to the wrapper if it exists.
    native_wrapper_->SetFocus();
  } else {
    // If there is no wrapper, cause the RootView to be focused so that we still
    // get keyboard messages.
    View::Focus();
  }
}

void Textfield::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
  if (is_add && !native_wrapper_ && GetWidget() && !initialized_) {
    initialized_ = true;
    native_wrapper_ = NativeTextfieldWrapper::CreateWrapper(this);
    //AddChildView(native_wrapper_->GetView());
    // TODO(beng): Move this initialization to NativeTextfieldWin once it
    //             subclasses NativeControlWin.
    native_wrapper_->UpdateText();
    native_wrapper_->UpdateBackgroundColor();
    native_wrapper_->UpdateReadOnly();
    native_wrapper_->UpdateFont();
    native_wrapper_->UpdateEnabled();
    native_wrapper_->UpdateBorder();

    // We need to call Layout here because any previous calls to Layout
    // will have short-circuited and we don't call AddChildView.
    Layout();
  }
}

std::string Textfield::GetClassName() const {
  return kViewClassName;
}

}  // namespace views
<|endoftext|>


<commit_before>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#include "../../core/Setup.h"

#if OUZEL_COMPILE_OPENAL

#if defined(__APPLE__)
#  include <TargetConditionals.h>
#endif
#if TARGET_OS_IOS || TARGET_OS_TV
#  include <objc/message.h>
#  include <objc/NSObjCRuntime.h>
extern "C" id const AVAudioSessionCategoryAmbient;
#endif

#include "OALAudioDevice.hpp"
#include "OALErrorCategory.hpp"
#include "ALCErrorCategory.hpp"
#include "../../core/Engine.hpp"
#include "../../utils/Log.hpp"

#ifndef AL_FORMAT_MONO_FLOAT32
#  define AL_FORMAT_MONO_FLOAT32 0x10010
#endif

#ifndef AL_FORMAT_STEREO_FLOAT32
#  define AL_FORMAT_STEREO_FLOAT32 0x10011
#endif

namespace ouzel::audio::openal
{
    namespace
    {
        const ErrorCategory openALErrorCategory{};
        const alc::ErrorCategory alcErrorCategory{};
    }

    AudioDevice::AudioDevice(const Settings& settings,
                             const std::function<void(std::uint32_t frames,
                                                      std::uint32_t channels,
                                                      std::uint32_t sampleRate,
                                                      std::vector<float>& samples)>& initDataGetter):
        audio::AudioDevice(Driver::openAL, settings, initDataGetter)
    {
#if TARGET_OS_IOS || TARGET_OS_TV
        id audioSession = reinterpret_cast<id (*)(Class, SEL)>(&objc_msgSend)(objc_getClass("AVAudioSession"), sel_getUid("sharedInstance")); // [AVAudioSession sharedInstance]
        if (!reinterpret_cast<BOOL (*)(id, SEL, id, id)>(&objc_msgSend)(audioSession, sel_getUid("setCategory:error:"), AVAudioSessionCategoryAmbient, nil)) // [audioSession setCategory:AVAudioSessionCategoryAmbient error:nil]
            throw std::runtime_error("Failed to set audio session category");

        id currentRoute = reinterpret_cast<id (*)(id, SEL)>(&objc_msgSend)(audioSession, sel_getUid("currentRoute")); // [audioSession currentRoute]
        id outputs = reinterpret_cast<id (*)(id, SEL)>(&objc_msgSend)(currentRoute, sel_getUid("outputs")); // [currentRoute outputs]
        const auto count = reinterpret_cast<NSUInteger (*)(id, SEL)>(&objc_msgSend)(outputs, sel_getUid("count")); // [outputs count]

        NSUInteger maxChannelCount = 0;
        for (NSUInteger outputIndex = 0; outputIndex < count; ++outputIndex)
        {
            id output = reinterpret_cast<id (*)(id, SEL, NSUInteger)>(&objc_msgSend)(outputs, sel_getUid("objectAtIndex:"), outputIndex); // [outputs objectAtIndex:outputIndex]
            id channels = reinterpret_cast<id (*)(id, SEL)>(&objc_msgSend)(output, sel_getUid("channels")); // [output channels]
            const auto channelCount = reinterpret_cast<NSUInteger (*)(id, SEL)>(&objc_msgSend)(channels, sel_getUid("count")); // [channels count]
            if (channelCount > maxChannelCount)
                maxChannelCount = channelCount;
        }

        if (channels > maxChannelCount)
            channels = static_cast<std::uint32_t>(maxChannelCount);
#endif

        const auto deviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);

        logger.log(Log::Level::info) << "Using " << deviceName << " for audio";

        device = alcOpenDevice(deviceName);

        if (!device)
            throw std::runtime_error("Failed to open ALC device");

        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to create ALC device");

        ALCint majorVersion;
        alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(majorVersion), &majorVersion);
        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to get major version");

        apiMajorVersion = static_cast<std::uint16_t>(majorVersion);

        ALCint minorVersion;
        alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(minorVersion), &minorVersion);
        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to get minor version");

        apiMinorVersion = static_cast<std::uint16_t>(minorVersion);

        logger.log(Log::Level::info) << "OpenAL version " << apiMajorVersion << '.' << apiMinorVersion;

        context = alcCreateContext(device, nullptr);

        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to create ALC context");

        if (!context)
            throw std::runtime_error("Failed to create ALC context");

        if (!alcMakeContextCurrent(context))
            throw std::runtime_error("Failed to make ALC context current");

        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to make ALC context current");

        const auto audioRenderer = alGetString(AL_RENDERER);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            logger.log(Log::Level::warning) << "Failed to get OpenAL renderer, error: " + std::to_string(error);
        else if (!audioRenderer)
            logger.log(Log::Level::warning) << "Failed to get OpenAL renderer";
        else
            logger.log(Log::Level::info) << "Using " << audioRenderer << " audio renderer";

        std::vector<std::string> extensions;
        const auto extensionsPtr = alGetString(AL_EXTENSIONS);

        if (const auto error = alGetError(); error != AL_NO_ERROR || !extensionsPtr)
            logger.log(Log::Level::warning) << "Failed to get OpenGL extensions";
        else
            extensions = explodeString(std::string(extensionsPtr), ' ');

        logger.log(Log::Level::all) << "Supported OpenAL extensions: " << extensions;

        auto float32Supported = false;
        for (const std::string& extension : extensions)
        {
            if (extension == "AL_EXT_float32")
                float32Supported = true;
        }

        alGenSources(1, &sourceId);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL source");

        alGenBuffers(static_cast<ALsizei>(bufferIds.size()),
                     bufferIds.data());

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL buffers");

        switch (channels)
        {
            case 1:
            {
                if (float32Supported)
                {
                    format = AL_FORMAT_MONO_FLOAT32;
                    sampleFormat = SampleFormat::float32;
                    sampleSize = sizeof(float);
                }
                else
                {
                    format = AL_FORMAT_MONO16;
                    sampleFormat = SampleFormat::signedInt16;
                    sampleSize = sizeof(std::int16_t);
                }
                break;
            }
            case 2:
            {
                if (float32Supported)
                {
                    format = AL_FORMAT_STEREO_FLOAT32;
                    sampleFormat = SampleFormat::float32;
                    sampleSize = sizeof(float);
                }
                else
                {
                    format = AL_FORMAT_STEREO16;
                    sampleFormat = SampleFormat::signedInt16;
                    sampleSize = sizeof(std::int16_t);
                }
                break;
            }
            case 4:
            {
                format = alGetEnumValue("AL_FORMAT_QUAD16");
                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL enum value");
                sampleFormat = SampleFormat::signedInt16;
                sampleSize = sizeof(std::int16_t);
                break;
            }
            case 6:
            {
                format = alGetEnumValue("AL_FORMAT_51CHN16");
                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL enum value");
                sampleFormat = SampleFormat::signedInt16;
                sampleSize = sizeof(std::int16_t);
                break;
            }
            case 7:
            {
                format = alGetEnumValue("AL_FORMAT_61CHN16");
                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL enum value");
                sampleFormat = SampleFormat::signedInt16;
                sampleSize = sizeof(std::int16_t);
                break;
            }
            case 8:
            {
                format = alGetEnumValue("AL_FORMAT_71CHN16");
                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL enum value");
                sampleFormat = SampleFormat::signedInt16;
                sampleSize = sizeof(std::int16_t);
                break;
            }
            default:
                throw std::runtime_error("Invalid channel count");
        }
    }

    AudioDevice::~AudioDevice()
    {
#if !defined(__EMSCRIPTEN__)
        running = false;
        if (audioThread.isJoinable()) audioThread.join();
#endif

        if (context)
        {
            alcMakeContextCurrent(context);

            if (sourceId)
            {
                alSourceStop(sourceId);
                alSourcei(sourceId, AL_BUFFER, 0);
                alDeleteSources(1, &sourceId);
                alGetError();
            }

            for (const auto bufferId : bufferIds)
            {
                if (bufferId)
                {
                    alDeleteBuffers(1, &bufferId);
                    alGetError();
                }
            }

            alcMakeContextCurrent(nullptr);
            alcDestroyContext(context);
        }

        if (device)
            alcCloseDevice(device);
    }

    void AudioDevice::start()
    {
        getData(bufferSize / (channels * sampleSize), data);

        alBufferData(bufferIds[0], format,
                     data.data(),
                     static_cast<ALsizei>(data.size()),
                     static_cast<ALsizei>(sampleRate));

        getData(bufferSize / (channels * sampleSize), data);

        alBufferData(bufferIds[1], format,
                     data.data(),
                     static_cast<ALsizei>(data.size()),
                     static_cast<ALsizei>(sampleRate));

        alSourceQueueBuffers(sourceId,
                             static_cast<ALsizei>(bufferIds.size()),
                             bufferIds.data());

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to queue OpenAL buffers");

        alSourcePlay(sourceId);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to play OpenAL source");

        if (!alcMakeContextCurrent(nullptr))
            throw std::runtime_error("Failed to unset current ALC context");

#if !defined(__EMSCRIPTEN__)
        running = true;
        audioThread = thread::Thread(&AudioDevice::run, this);
#endif
    }

    void AudioDevice::stop()
    {
#if !defined(__EMSCRIPTEN__)
        running = false;
        if (audioThread.isJoinable()) audioThread.join();
#endif

        alSourceStop(sourceId);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to stop OpenAL source");
    }

    void AudioDevice::process()
    {
        ALint buffersProcessed;
        alGetSourcei(sourceId, AL_BUFFERS_PROCESSED, &buffersProcessed);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to get processed buffer count");

        // requeue all processed buffers
        for (ALint i = 0; i < buffersProcessed; ++i)
        {
            ALuint buffer;
            alSourceUnqueueBuffers(sourceId, 1, &buffer);

            if (const auto error = alGetError(); error != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to unqueue OpenAL buffer");

            getData(bufferSize / (channels * sampleSize), data);

            alBufferData(buffer, format,
                         data.data(),
                         static_cast<ALsizei>(data.size()),
                         static_cast<ALsizei>(sampleRate));

            alSourceQueueBuffers(sourceId, 1, &buffer);

            if (const auto error = alGetError(); error != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to queue OpenAL buffer");
        }

        if (buffersProcessed == bufferIds.size())
        {
            ALint state;
            alGetSourcei(sourceId, AL_SOURCE_STATE, &state);
            if (state != AL_PLAYING)
            {
                alSourcePlay(sourceId);

                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to play OpenAL source");
            }
        }
    }

    void AudioDevice::run()
    {
        thread::setCurrentThreadName("Audio");

        if (!alcMakeContextCurrent(context))
            throw std::runtime_error("Failed to make ALC context current");

        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to make ALC context current");

#if !defined(__EMSCRIPTEN__)
        while (running)
        {
            try
            {
                process();
            }
            catch (const std::exception& e)
            {
                ouzel::logger.log(ouzel::Log::Level::error) << e.what();
            }
        }
#endif
    }
}
#endif
<commit_msg>Cast the buffer ids size to ALint to silence warnings<commit_after>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#include "../../core/Setup.h"

#if OUZEL_COMPILE_OPENAL

#if defined(__APPLE__)
#  include <TargetConditionals.h>
#endif
#if TARGET_OS_IOS || TARGET_OS_TV
#  include <objc/message.h>
#  include <objc/NSObjCRuntime.h>
extern "C" id const AVAudioSessionCategoryAmbient;
#endif

#include "OALAudioDevice.hpp"
#include "OALErrorCategory.hpp"
#include "ALCErrorCategory.hpp"
#include "../../core/Engine.hpp"
#include "../../utils/Log.hpp"

#ifndef AL_FORMAT_MONO_FLOAT32
#  define AL_FORMAT_MONO_FLOAT32 0x10010
#endif

#ifndef AL_FORMAT_STEREO_FLOAT32
#  define AL_FORMAT_STEREO_FLOAT32 0x10011
#endif

namespace ouzel::audio::openal
{
    namespace
    {
        const ErrorCategory openALErrorCategory{};
        const alc::ErrorCategory alcErrorCategory{};
    }

    AudioDevice::AudioDevice(const Settings& settings,
                             const std::function<void(std::uint32_t frames,
                                                      std::uint32_t channels,
                                                      std::uint32_t sampleRate,
                                                      std::vector<float>& samples)>& initDataGetter):
        audio::AudioDevice(Driver::openAL, settings, initDataGetter)
    {
#if TARGET_OS_IOS || TARGET_OS_TV
        id audioSession = reinterpret_cast<id (*)(Class, SEL)>(&objc_msgSend)(objc_getClass("AVAudioSession"), sel_getUid("sharedInstance")); // [AVAudioSession sharedInstance]
        if (!reinterpret_cast<BOOL (*)(id, SEL, id, id)>(&objc_msgSend)(audioSession, sel_getUid("setCategory:error:"), AVAudioSessionCategoryAmbient, nil)) // [audioSession setCategory:AVAudioSessionCategoryAmbient error:nil]
            throw std::runtime_error("Failed to set audio session category");

        id currentRoute = reinterpret_cast<id (*)(id, SEL)>(&objc_msgSend)(audioSession, sel_getUid("currentRoute")); // [audioSession currentRoute]
        id outputs = reinterpret_cast<id (*)(id, SEL)>(&objc_msgSend)(currentRoute, sel_getUid("outputs")); // [currentRoute outputs]
        const auto count = reinterpret_cast<NSUInteger (*)(id, SEL)>(&objc_msgSend)(outputs, sel_getUid("count")); // [outputs count]

        NSUInteger maxChannelCount = 0;
        for (NSUInteger outputIndex = 0; outputIndex < count; ++outputIndex)
        {
            id output = reinterpret_cast<id (*)(id, SEL, NSUInteger)>(&objc_msgSend)(outputs, sel_getUid("objectAtIndex:"), outputIndex); // [outputs objectAtIndex:outputIndex]
            id channels = reinterpret_cast<id (*)(id, SEL)>(&objc_msgSend)(output, sel_getUid("channels")); // [output channels]
            const auto channelCount = reinterpret_cast<NSUInteger (*)(id, SEL)>(&objc_msgSend)(channels, sel_getUid("count")); // [channels count]
            if (channelCount > maxChannelCount)
                maxChannelCount = channelCount;
        }

        if (channels > maxChannelCount)
            channels = static_cast<std::uint32_t>(maxChannelCount);
#endif

        const auto deviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);

        logger.log(Log::Level::info) << "Using " << deviceName << " for audio";

        device = alcOpenDevice(deviceName);

        if (!device)
            throw std::runtime_error("Failed to open ALC device");

        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to create ALC device");

        ALCint majorVersion;
        alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(majorVersion), &majorVersion);
        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to get major version");

        apiMajorVersion = static_cast<std::uint16_t>(majorVersion);

        ALCint minorVersion;
        alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(minorVersion), &minorVersion);
        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to get minor version");

        apiMinorVersion = static_cast<std::uint16_t>(minorVersion);

        logger.log(Log::Level::info) << "OpenAL version " << apiMajorVersion << '.' << apiMinorVersion;

        context = alcCreateContext(device, nullptr);

        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to create ALC context");

        if (!context)
            throw std::runtime_error("Failed to create ALC context");

        if (!alcMakeContextCurrent(context))
            throw std::runtime_error("Failed to make ALC context current");

        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to make ALC context current");

        const auto audioRenderer = alGetString(AL_RENDERER);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            logger.log(Log::Level::warning) << "Failed to get OpenAL renderer, error: " + std::to_string(error);
        else if (!audioRenderer)
            logger.log(Log::Level::warning) << "Failed to get OpenAL renderer";
        else
            logger.log(Log::Level::info) << "Using " << audioRenderer << " audio renderer";

        std::vector<std::string> extensions;
        const auto extensionsPtr = alGetString(AL_EXTENSIONS);

        if (const auto error = alGetError(); error != AL_NO_ERROR || !extensionsPtr)
            logger.log(Log::Level::warning) << "Failed to get OpenGL extensions";
        else
            extensions = explodeString(std::string(extensionsPtr), ' ');

        logger.log(Log::Level::all) << "Supported OpenAL extensions: " << extensions;

        auto float32Supported = false;
        for (const std::string& extension : extensions)
        {
            if (extension == "AL_EXT_float32")
                float32Supported = true;
        }

        alGenSources(1, &sourceId);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL source");

        alGenBuffers(static_cast<ALsizei>(bufferIds.size()),
                     bufferIds.data());

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL buffers");

        switch (channels)
        {
            case 1:
            {
                if (float32Supported)
                {
                    format = AL_FORMAT_MONO_FLOAT32;
                    sampleFormat = SampleFormat::float32;
                    sampleSize = sizeof(float);
                }
                else
                {
                    format = AL_FORMAT_MONO16;
                    sampleFormat = SampleFormat::signedInt16;
                    sampleSize = sizeof(std::int16_t);
                }
                break;
            }
            case 2:
            {
                if (float32Supported)
                {
                    format = AL_FORMAT_STEREO_FLOAT32;
                    sampleFormat = SampleFormat::float32;
                    sampleSize = sizeof(float);
                }
                else
                {
                    format = AL_FORMAT_STEREO16;
                    sampleFormat = SampleFormat::signedInt16;
                    sampleSize = sizeof(std::int16_t);
                }
                break;
            }
            case 4:
            {
                format = alGetEnumValue("AL_FORMAT_QUAD16");
                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL enum value");
                sampleFormat = SampleFormat::signedInt16;
                sampleSize = sizeof(std::int16_t);
                break;
            }
            case 6:
            {
                format = alGetEnumValue("AL_FORMAT_51CHN16");
                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL enum value");
                sampleFormat = SampleFormat::signedInt16;
                sampleSize = sizeof(std::int16_t);
                break;
            }
            case 7:
            {
                format = alGetEnumValue("AL_FORMAT_61CHN16");
                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL enum value");
                sampleFormat = SampleFormat::signedInt16;
                sampleSize = sizeof(std::int16_t);
                break;
            }
            case 8:
            {
                format = alGetEnumValue("AL_FORMAT_71CHN16");
                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to create OpenAL enum value");
                sampleFormat = SampleFormat::signedInt16;
                sampleSize = sizeof(std::int16_t);
                break;
            }
            default:
                throw std::runtime_error("Invalid channel count");
        }
    }

    AudioDevice::~AudioDevice()
    {
#if !defined(__EMSCRIPTEN__)
        running = false;
        if (audioThread.isJoinable()) audioThread.join();
#endif

        if (context)
        {
            alcMakeContextCurrent(context);

            if (sourceId)
            {
                alSourceStop(sourceId);
                alSourcei(sourceId, AL_BUFFER, 0);
                alDeleteSources(1, &sourceId);
                alGetError();
            }

            for (const auto bufferId : bufferIds)
            {
                if (bufferId)
                {
                    alDeleteBuffers(1, &bufferId);
                    alGetError();
                }
            }

            alcMakeContextCurrent(nullptr);
            alcDestroyContext(context);
        }

        if (device)
            alcCloseDevice(device);
    }

    void AudioDevice::start()
    {
        getData(bufferSize / (channels * sampleSize), data);

        alBufferData(bufferIds[0], format,
                     data.data(),
                     static_cast<ALsizei>(data.size()),
                     static_cast<ALsizei>(sampleRate));

        getData(bufferSize / (channels * sampleSize), data);

        alBufferData(bufferIds[1], format,
                     data.data(),
                     static_cast<ALsizei>(data.size()),
                     static_cast<ALsizei>(sampleRate));

        alSourceQueueBuffers(sourceId,
                             static_cast<ALsizei>(bufferIds.size()),
                             bufferIds.data());

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to queue OpenAL buffers");

        alSourcePlay(sourceId);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to play OpenAL source");

        if (!alcMakeContextCurrent(nullptr))
            throw std::runtime_error("Failed to unset current ALC context");

#if !defined(__EMSCRIPTEN__)
        running = true;
        audioThread = thread::Thread(&AudioDevice::run, this);
#endif
    }

    void AudioDevice::stop()
    {
#if !defined(__EMSCRIPTEN__)
        running = false;
        if (audioThread.isJoinable()) audioThread.join();
#endif

        alSourceStop(sourceId);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to stop OpenAL source");
    }

    void AudioDevice::process()
    {
        ALint buffersProcessed;
        alGetSourcei(sourceId, AL_BUFFERS_PROCESSED, &buffersProcessed);

        if (const auto error = alGetError(); error != AL_NO_ERROR)
            throw std::system_error(error, openALErrorCategory, "Failed to get processed buffer count");

        // requeue all processed buffers
        for (ALint i = 0; i < buffersProcessed; ++i)
        {
            ALuint buffer;
            alSourceUnqueueBuffers(sourceId, 1, &buffer);

            if (const auto error = alGetError(); error != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to unqueue OpenAL buffer");

            getData(bufferSize / (channels * sampleSize), data);

            alBufferData(buffer, format,
                         data.data(),
                         static_cast<ALsizei>(data.size()),
                         static_cast<ALsizei>(sampleRate));

            alSourceQueueBuffers(sourceId, 1, &buffer);

            if (const auto error = alGetError(); error != AL_NO_ERROR)
                throw std::system_error(error, openALErrorCategory, "Failed to queue OpenAL buffer");
        }

        if (buffersProcessed == static_cast<ALint>(bufferIds.size()))
        {
            ALint state;
            alGetSourcei(sourceId, AL_SOURCE_STATE, &state);
            if (state != AL_PLAYING)
            {
                alSourcePlay(sourceId);

                if (const auto error = alGetError(); error != AL_NO_ERROR)
                    throw std::system_error(error, openALErrorCategory, "Failed to play OpenAL source");
            }
        }
    }

    void AudioDevice::run()
    {
        thread::setCurrentThreadName("Audio");

        if (!alcMakeContextCurrent(context))
            throw std::runtime_error("Failed to make ALC context current");

        if (const auto error = alcGetError(device); error != ALC_NO_ERROR)
            throw std::system_error(error, alcErrorCategory, "Failed to make ALC context current");

#if !defined(__EMSCRIPTEN__)
        while (running)
        {
            try
            {
                process();
            }
            catch (const std::exception& e)
            {
                ouzel::logger.log(ouzel::Log::Level::error) << e.what();
            }
        }
#endif
    }
}
#endif
<|endoftext|>


<commit_before>#include "Players/Alan_Turing_AI.h"

#include <vector>
#include <cmath>

#include "Game/Board.h"
#include "Moves/Move.h"
#include "Pieces/Piece.h"
#include "Game/Game_Result.h"
#include "Game/Color.h"
#include "Moves/Threat_Generator.h"

const Move& Alan_Turing_AI::choose_move(const Board& board, const Clock&) const
{
    // Every possible first move is considerable
    std::pair<double, double> best_first_move_score = {-1001.0, -1001.0}; // maximize
    auto best_first_move = board.legal_moves().front();
    for(auto first_move : board.legal_moves())
    {
        auto first_board = board;
        auto first_move_result = first_board.submit_move(*first_move);
        std::pair<double, double> first_move_score;
        if(first_move_result.game_has_ended())
        {
            first_move_score = position_value(first_board, board.whose_turn(), first_move_result);
        }
        else
        {
            // Every possible reply is considerable
            std::pair<double, double> worst_second_move_score = {1001.0, 1001.0}; // minimize
            for(auto second_move : first_board.legal_moves())
            {
                auto second_board = first_board;
                std::pair<double, double> second_move_score;
                auto second_move_result = second_board.submit_move(*second_move);
                if(second_move_result.game_has_ended())
                {
                    second_move_score = position_value(second_board, board.whose_turn(), second_move_result);
                }
                else
                {
                    std::pair<double, double> best_third_move_score = {-1001.0, -1001.0}; // maximize
                    for(auto third_move : second_board.legal_moves())
                    {
                        auto third_board = second_board;
                        auto third_move_result = third_board.submit_move(*third_move);
                        auto third_move_score = position_value(third_board, board.whose_turn(), third_move_result);
                        best_third_move_score = std::max(best_third_move_score, third_move_score);
                    }

                    second_move_score = best_third_move_score;
                }

                worst_second_move_score = std::min(worst_second_move_score, second_move_score);
            }

            first_move_score = worst_second_move_score;
        }

        if(first_move_score > best_first_move_score)
        {
            best_first_move = first_move;
            best_first_move_score = first_move_score;
        }
    }

    return *best_first_move;
}

std::string Alan_Turing_AI::name() const
{
    return "Turbocomp";
}

std::string Alan_Turing_AI::author() const
{
    return "Alan Turing";
}

std::vector<const Move*> Alan_Turing_AI::considerable_moves(const Board& board) const
{
    std::vector<const Move*> result;
    for(auto move : board.legal_moves())
    {
        if(is_considerable(*move, board))
        {
            result.push_back(move);
        }
    }

    return result;
}

bool Alan_Turing_AI::is_considerable(const Move& move, const Board& board) const
{
    // Recapture is considerable
    if(board.last_move_captured() && board.move_captures(move))
    {
        auto last_move = board.game_record().back();
        if(last_move->end_file() == move.end_file() && last_move->end_rank() == move.end_rank())
        {
            return true;
        }
    }

    auto attacking_piece = board.piece_on_square(move.start_file(), move.start_rank());
    auto attacked_piece = board.piece_on_square(move.end_file(), move.end_rank());
    if(attacked_piece)
    {
        // Capturing an undefended piece is considerable
        auto temp_board = board;
        auto result = temp_board.submit_move(move);
        if(temp_board.safe_for_king(move.end_file(), move.end_rank(), attacking_piece->color()))
        {
            return true;
        }

        // Capturing with a less valuable piece is considerable
        if(piece_value(attacked_piece) > piece_value(attacking_piece))
        {
            return true;
        }

        // A mvoe resulting in checkmate is considerable
        if(result.winner() == board.whose_turn())
        {
            return true;
        }
    }

    return false;
}

double Alan_Turing_AI::material_value(const Board& board, Color perspective) const
{
    double player_score = 0.0;
    double opponent_score = 0.0;

    for(char file = 'a'; file <= 'h'; ++file)
    {
        for(int rank = 1; rank <= 8; ++rank)
        {
            auto piece = board.piece_on_square(file, rank);
            if(piece)
            {
                if(piece->color() == perspective)
                {
                    player_score += piece_value(piece);
                }
                else
                {
                    opponent_score += piece_value(piece);
                }
            }
        }
    }

    return player_score/opponent_score;
}

double Alan_Turing_AI::piece_value(const Piece* piece) const
{
    if(!piece)
    {
        return 0.0;
    }

    switch(piece->type())
    {
        case PAWN:
            return 1.0;

        case KNIGHT:
            return 3.0;

        case BISHOP:
            return 3.5;

        case ROOK:
            return 5.0;

        case QUEEN:
            return 10.0;

        default:
            return 0.0;
    }
}

double Alan_Turing_AI::position_play_value(const Board& board, Color perspective) const
{
    double total_score = 0.0;

    for(char file = 'a'; file <= 'h'; ++file)
    {
        for(int rank = 1; rank <= 8; ++rank)
        {
            auto piece = board.piece_on_square(file, rank);
            if(!piece)
            {
                continue;
            }

            if(piece->color() == perspective)
            {
                if(piece->type() == QUEEN || piece->type() == ROOK || piece->type() == BISHOP || piece->type() == KNIGHT)
                {
                    // Number of moves score
                    double move_score = 0.0;
                    for(auto move : board.legal_moves())
                    {
                        if(move->start_file() == file || move->start_rank() == rank)
                        {
                            move_score += 1.0;
                            if(board.move_captures(*move))
                            {
                                move_score += 1.0;
                            }
                        }
                    }
                    total_score += std::sqrt(move_score);

                    // Non-queen pieces defended
                    if(piece->type() != QUEEN)
                    {
                        Square defending_square{};
                        for(auto defender : Threat_Generator(file, rank, opposite(perspective), board))
                        {
                            if(defending_square)
                            {
                                total_score += 0.5;
                            }
                            else
                            {
                                total_score += 1.0;
                                defending_square = defender;
                            }
                        }
                    }
                }
                else if(piece->type() == KING)
                {
                    // King move scores
                    double move_score = 0.0;
                    for(auto move : board.legal_moves())
                    {
                        if(move->start_file() != file || move->start_rank() != rank)
                        {
                            continue;
                        }

                        if(std::abs(move->file_change()) < 2)
                        {
                            move_score += 1.0;
                        }
                    }
                    total_score += std::sqrt(move_score);

                    // King vulnerability
                    double king_squares = 0.0;
                    for(int file_step = -1; file_step <= 1; ++file_step)
                    {
                        for(int rank_step = -1; rank_step <= 1; ++rank_step)
                        {
                            if(file_step == 0 && rank_step == 0)
                            {
                                continue;
                            }

                            for(int steps = 1; steps <= 7; ++steps)
                            {
                                char attack_file = file + steps*file_step;
                                int  attack_rank = rank + steps*rank_step;

                                if(!board.inside_board(attack_file, attack_rank))
                                {
                                    break;
                                }

                                auto other_piece = board.piece_on_square(attack_file, attack_rank);
                                if( ! other_piece)
                                {
                                    king_squares += 1.0;
                                }
                                else
                                {
                                    if(other_piece->color() != perspective)
                                    {
                                        king_squares += 1.0;
                                    }

                                    break;
                                }
                            }
                        }
                    }
                    total_score -= std::sqrt(king_squares);

                    // Castline score
                    if(!board.piece_has_moved(file, rank))
                    {
                        // Queenside castling
                        if(!board.piece_has_moved('a', rank))
                        {
                            total_score += 1.0;

                            // Can castle on next move
                            if(board.all_empty_between('a', rank, file, rank))
                            {
                                total_score += 1.0;
                            }
                        }

                        // Kingside castling
                        if(!board.piece_has_moved('h', rank))
                        {
                            total_score += 1.0;

                            // Can castle on next move
                            if(board.all_empty_between('h', rank, file, rank))
                            {
                                total_score += 1.0;
                            }
                        }
                    }

                    // Last move was castling
                    if(board.castling_move_index(perspective) < board.game_record().size())
                    {
                        total_score += 1.0;
                    }
                }
                else if(piece->type() == PAWN)
                {
                    // Pawn advancement
                    auto base_rank = (perspective == WHITE ? 2 : 7);
                    total_score += 0.2*std::abs(base_rank - rank);

                    // Pawn defended
                    auto pawn_defended = false;
                    for(auto piece_type :{QUEEN, ROOK, BISHOP, KNIGHT, KING})
                    {
                        if(pawn_defended)
                        {
                            break;
                        }

                        auto defending_piece = board.piece_instance(piece_type, perspective);
                        for(auto move : defending_piece->move_list(file, rank))
                        {
                            if(defending_piece == board.piece_on_square(move->end_file(), move->end_rank()))
                            {
                                if(piece_type == KNIGHT || board.all_empty_between(file, rank, move->end_file(), move->end_rank()))
                                {
                                    pawn_defended = true;
                                    break;
                                }
                            }
                        }
                    }

                    if(pawn_defended)
                    {
                        total_score += 0.3;
                    }
                }
            }
            else // piece->color() == opposite(perpsective)
            {
                if(piece->type() == KING)
                {
                    auto temp_board = board;
                    temp_board.set_turn(opposite(perspective));
                    if(temp_board.king_is_in_check())
                    {
                        total_score += 0.5;
                    }
                    else
                    {
                        temp_board.set_turn(perspective);
                        for(auto move : temp_board.legal_moves())
                        {
                            auto temp_temp_board = temp_board;
                            if(temp_temp_board.submit_move(*move).winner() != NONE)
                            {
                                total_score += 1.0;
                            }
                        }
                    }
                }
            }
        }
    }

    return total_score;
}

std::pair<double, double> Alan_Turing_AI::position_value(const Board& board, Color perspective, const Game_Result& move_result) const
{
    auto best_score = score_board(board, perspective, move_result);
    if(move_result.game_has_ended())
    {
        return best_score;
    }

    auto considerable_move_list = considerable_moves(board);

    // Skip if every move is considerable
    if(considerable_move_list.size() < board.legal_moves().size())
    {
        for(auto move : considerable_moves(board))
        {
            auto temp_board = board;
            auto result = temp_board.submit_move(*move);
            best_score = std::max(best_score, score_board(temp_board, perspective, result));
        }
    }

    return best_score;
}

std::pair<double, double> Alan_Turing_AI::score_board(const Board& board, Color perspective, const Game_Result& move_result) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == perspective)
        {
            return {1000.0, 1000.0};
        }
        else if(move_result.winner() == opposite(perspective))
        {
            return {-1000.0, -1000.0};
        }
        else
        {
            return {0.0, 0.0};
        }
    }

    return std::make_pair(material_value(board, perspective), position_play_value(board, perspective));
}
<commit_msg>Fix errors in Alan Turing AI valuations<commit_after>#include "Players/Alan_Turing_AI.h"

#include <vector>
#include <cmath>

#include "Game/Board.h"
#include "Moves/Move.h"
#include "Pieces/Piece.h"
#include "Game/Game_Result.h"
#include "Game/Color.h"
#include "Moves/Threat_Generator.h"

const Move& Alan_Turing_AI::choose_move(const Board& board, const Clock&) const
{
    // Every possible first move is considerable
    std::pair<double, double> best_first_move_score = {-1001.0, -1001.0}; // maximize
    auto best_first_move = board.legal_moves().front();
    for(auto first_move : board.legal_moves())
    {
        auto first_board = board;
        auto first_move_result = first_board.submit_move(*first_move);
        std::pair<double, double> first_move_score;
        if(first_move_result.game_has_ended())
        {
            first_move_score = position_value(first_board, board.whose_turn(), first_move_result);
        }
        else
        {
            // Every possible reply is considerable
            std::pair<double, double> worst_second_move_score = {1001.0, 1001.0}; // minimize
            for(auto second_move : first_board.legal_moves())
            {
                auto second_board = first_board;
                std::pair<double, double> second_move_score;
                auto second_move_result = second_board.submit_move(*second_move);
                if(second_move_result.game_has_ended())
                {
                    second_move_score = position_value(second_board, board.whose_turn(), second_move_result);
                }
                else
                {
                    std::pair<double, double> best_third_move_score = {-1001.0, -1001.0}; // maximize
                    for(auto third_move : second_board.legal_moves())
                    {
                        auto third_board = second_board;
                        auto third_move_result = third_board.submit_move(*third_move);
                        auto third_move_score = position_value(third_board, board.whose_turn(), third_move_result);
                        best_third_move_score = std::max(best_third_move_score, third_move_score);
                    }

                    second_move_score = best_third_move_score;
                }

                worst_second_move_score = std::min(worst_second_move_score, second_move_score);
            }

            first_move_score = worst_second_move_score;
        }

        if(first_move_score > best_first_move_score)
        {
            best_first_move = first_move;
            best_first_move_score = first_move_score;
        }
    }

    return *best_first_move;
}

std::string Alan_Turing_AI::name() const
{
    return "Turbocomp";
}

std::string Alan_Turing_AI::author() const
{
    return "Alan Turing";
}

std::vector<const Move*> Alan_Turing_AI::considerable_moves(const Board& board) const
{
    std::vector<const Move*> result;
    for(auto move : board.legal_moves())
    {
        if(is_considerable(*move, board))
        {
            result.push_back(move);
        }
    }

    return result;
}

bool Alan_Turing_AI::is_considerable(const Move& move, const Board& board) const
{
    // Recapture is considerable
    if(board.last_move_captured() && board.move_captures(move))
    {
        auto last_move = board.game_record().back();
        if(last_move->end_file() == move.end_file() && last_move->end_rank() == move.end_rank())
        {
            return true;
        }
    }

    auto attacking_piece = board.piece_on_square(move.start_file(), move.start_rank());
    auto attacked_piece = board.piece_on_square(move.end_file(), move.end_rank());
    if(attacked_piece)
    {
        // Capturing an undefended piece is considerable
        auto temp_board = board;
        auto result = temp_board.submit_move(move);
        if(temp_board.safe_for_king(move.end_file(), move.end_rank(), attacking_piece->color()))
        {
            return true;
        }

        // Capturing with a less valuable piece is considerable
        if(piece_value(attacked_piece) > piece_value(attacking_piece))
        {
            return true;
        }

        // A mvoe resulting in checkmate is considerable
        if(result.winner() == board.whose_turn())
        {
            return true;
        }
    }

    return false;
}

double Alan_Turing_AI::material_value(const Board& board, Color perspective) const
{
    double player_score = 0.0;
    double opponent_score = 0.0;

    for(char file = 'a'; file <= 'h'; ++file)
    {
        for(int rank = 1; rank <= 8; ++rank)
        {
            auto piece = board.piece_on_square(file, rank);
            if(piece)
            {
                if(piece->color() == perspective)
                {
                    player_score += piece_value(piece);
                }
                else
                {
                    opponent_score += piece_value(piece);
                }
            }
        }
    }

    return player_score/opponent_score;
}

double Alan_Turing_AI::piece_value(const Piece* piece) const
{
    if(!piece)
    {
        return 0.0;
    }

    switch(piece->type())
    {
        case PAWN:
            return 1.0;

        case KNIGHT:
            return 3.0;

        case BISHOP:
            return 3.5;

        case ROOK:
            return 5.0;

        case QUEEN:
            return 10.0;

        default:
            return 0.0;
    }
}

double Alan_Turing_AI::position_play_value(const Board& board, Color perspective) const
{
    double total_score = 0.0;

    for(char file = 'a'; file <= 'h'; ++file)
    {
        for(int rank = 1; rank <= 8; ++rank)
        {
            auto piece = board.piece_on_square(file, rank);
            if(!piece)
            {
                continue;
            }

            if(piece->color() == perspective)
            {
                if(piece->type() == QUEEN || piece->type() == ROOK || piece->type() == BISHOP || piece->type() == KNIGHT)
                {
                    // Number of moves score
                    double move_score = 0.0;
                    for(auto move : board.legal_moves())
                    {
                        if(move->start_file() == file && move->start_rank() == rank)
                        {
                            move_score += 1.0;
                            if(board.move_captures(*move))
                            {
                                move_score += 1.0;
                            }
                        }
                    }
                    total_score += std::sqrt(move_score);

                    // Non-queen pieces defended
                    if(piece->type() != QUEEN)
                    {
                        Square defending_square{};
                        for(auto defender : Threat_Generator(file, rank, opposite(perspective), board))
                        {
                            if(defending_square)
                            {
                                total_score += 0.5;
                            }
                            else
                            {
                                total_score += 1.0;
                                defending_square = defender;
                            }
                        }
                    }
                }
                else if(piece->type() == KING)
                {
                    // King move scores
                    double move_score = 0.0;
                    double castling_moves = 0.0;
                    for(auto move : board.legal_moves())
                    {
                        if(move->start_file() != file || move->start_rank() != rank)
                        {
                            continue;
                        }

                        if(move->is_castling())
                        {
                            castling_moves += 1.0;
                        }
                        else
                        {
                            move_score += 1.0;
                        }
                    }
                    total_score += std::sqrt(move_score) + castling_moves;

                    // King vulnerability (count number of queen moves from king square and subtract)
                    double king_squares = 0.0;
                    for(int file_step = -1; file_step <= 1; ++file_step)
                    {
                        for(int rank_step = -1; rank_step <= 1; ++rank_step)
                        {
                            if(file_step == 0 && rank_step == 0)
                            {
                                continue;
                            }

                            for(int steps = 1; steps <= 7; ++steps)
                            {
                                char attack_file = file + steps*file_step;
                                int  attack_rank = rank + steps*rank_step;

                                if(!board.inside_board(attack_file, attack_rank))
                                {
                                    break;
                                }

                                auto other_piece = board.piece_on_square(attack_file, attack_rank);
                                if( ! other_piece)
                                {
                                    king_squares += 1.0;
                                }
                                else
                                {
                                    if(other_piece->color() != perspective)
                                    {
                                        king_squares += 1.0;
                                    }

                                    break;
                                }
                            }
                        }
                    }
                    total_score -= std::sqrt(king_squares);

                    // Castline score
                    if(!board.piece_has_moved(file, rank))
                    {
                        // Queenside castling
                        if(!board.piece_has_moved('a', rank))
                        {
                            total_score += 1.0;

                            // Can castle on next move
                            if(board.all_empty_between('a', rank, file, rank))
                            {
                                total_score += 1.0;
                            }
                        }

                        // Kingside castling
                        if(!board.piece_has_moved('h', rank))
                        {
                            total_score += 1.0;

                            // Can castle on next move
                            if(board.all_empty_between('h', rank, file, rank))
                            {
                                total_score += 1.0;
                            }
                        }
                    }

                    // Last move was castling
                    if(board.castling_move_index(perspective) < board.game_record().size())
                    {
                        total_score += 1.0;
                    }
                }
                else if(piece->type() == PAWN)
                {
                    // Pawn advancement
                    auto base_rank = (perspective == WHITE ? 2 : 7);
                    total_score += 0.2*std::abs(base_rank - rank);

                    // Pawn defended
                    auto pawn_defended = false;
                    for(auto piece_type :{QUEEN, ROOK, BISHOP, KNIGHT, KING})
                    {
                        if(pawn_defended)
                        {
                            break;
                        }

                        auto defending_piece = board.piece_instance(piece_type, perspective);
                        for(auto move : defending_piece->move_list(file, rank))
                        {
                            if(defending_piece == board.piece_on_square(move->end_file(), move->end_rank()))
                            {
                                if(piece_type == KNIGHT || board.all_empty_between(file, rank, move->end_file(), move->end_rank()))
                                {
                                    pawn_defended = true;
                                    break;
                                }
                            }
                        }
                    }

                    if(pawn_defended)
                    {
                        total_score += 0.3;
                    }
                }
            }
            else // piece->color() == opposite(perpsective)
            {
                if(piece->type() == KING)
                {
                    auto temp_board = board;
                    temp_board.set_turn(opposite(perspective));
                    if(temp_board.king_is_in_check())
                    {
                        total_score += 0.5;
                    }
                    else
                    {
                        temp_board.set_turn(perspective);
                        for(auto move : temp_board.legal_moves())
                        {
                            auto temp_temp_board = temp_board;
                            if(temp_temp_board.submit_move(*move).winner() != NONE)
                            {
                                total_score += 1.0;
                            }
                        }
                    }
                }
            }
        }
    }

    return total_score;
}

std::pair<double, double> Alan_Turing_AI::position_value(const Board& board, Color perspective, const Game_Result& move_result) const
{
    auto best_score = score_board(board, perspective, move_result);
    if(move_result.game_has_ended())
    {
        return best_score;
    }

    auto considerable_move_list = considerable_moves(board);

    // Skip if every move is considerable
    if(considerable_move_list.size() < board.legal_moves().size())
    {
        for(auto move : considerable_moves(board))
        {
            auto temp_board = board;
            auto result = temp_board.submit_move(*move);
            best_score = std::max(best_score, score_board(temp_board, perspective, result));
        }
    }

    return best_score;
}

std::pair<double, double> Alan_Turing_AI::score_board(const Board& board, Color perspective, const Game_Result& move_result) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == perspective)
        {
            return {1000.0, 1000.0};
        }
        else if(move_result.winner() == opposite(perspective))
        {
            return {-1000.0, -1000.0};
        }
        else
        {
            return {0.0, 0.0};
        }
    }

    return std::make_pair(material_value(board, perspective), position_play_value(board, perspective));
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016+     AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
 * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 */

#include "blackwing_lair.h"
#include "GameObject.h"
#include "InstanceScript.h"
#include "Map.h"
#include "MotionMaster.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "TemporarySummon.h"

DoorData const doorData[] =
{
    { GO_PORTCULLIS_RAZORGORE,    DATA_RAZORGORE_THE_UNTAMED,  DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 175946 || GUID 7230
    { GO_PORTCULLIS_VAELASTRASZ,  DATA_VAELASTRAZ_THE_CORRUPT, DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 175185 || GUID 7229
    { GO_PORTCULLIS_BROODLORD,    DATA_BROODLORD_LASHLAYER,    DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179365 || GUID 75159
    { GO_PORTCULLIS_THREEDRAGONS, DATA_FIREMAW,                DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179115 || GUID 75165
    { GO_PORTCULLIS_THREEDRAGONS, DATA_EBONROC,                DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179115 || GUID 75165
    { GO_PORTCULLIS_THREEDRAGONS, DATA_FLAMEGOR,               DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179115 || GUID 75165
    { GO_PORTCULLIS_CHROMAGGUS,   DATA_CHROMAGGUS,             DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179116 || GUID 75161
    { GO_PORTCULLIS_NEFARIAN,     DATA_NEFARIAN,               DOOR_TYPE_ROOM, BOUNDARY_NONE},    // ID 179117 || GUID 75164
    { 0,                         0,                            DOOR_TYPE_ROOM, BOUNDARY_NONE}     // END
};

Position const SummonPosition[8] =
{
    {-7661.207520f, -1043.268188f, 407.199554f, 6.280452f},
    {-7644.145020f, -1065.628052f, 407.204956f, 0.501492f},
    {-7624.260742f, -1095.196899f, 407.205017f, 0.544694f},
    {-7608.501953f, -1116.077271f, 407.199921f, 0.816443f},
    {-7531.841797f, -1063.765381f, 407.199615f, 2.874187f},
    {-7547.319336f, -1040.971924f, 407.205078f, 3.789175f},
    {-7568.547852f, -1013.112488f, 407.204926f, 3.773467f},
    {-7584.175781f, -989.6691289f, 407.199585f, 4.527447f},
};

uint32 const Entry[5] = {12422, 12458, 12416, 12420, 12459};

class instance_blackwing_lair : public InstanceMapScript
{
public:
    instance_blackwing_lair() : InstanceMapScript(BWLScriptName, 469) { }

    struct instance_blackwing_lair_InstanceMapScript : public InstanceScript
    {
        instance_blackwing_lair_InstanceMapScript(Map* map) : InstanceScript(map)
        {
            //SetHeaders(DataHeader);
            SetBossNumber(EncounterCount);
            LoadDoorData(doorData);
            //LoadObjectData(creatureData, gameObjectData);
        }

        void Initialize() override
        {
            // Razorgore
            EggCount = 0;
            EggEvent = 0;
        }

        void OnCreatureCreate(Creature* creature) override
        {
            InstanceScript::OnCreatureCreate(creature);

            switch (creature->GetEntry())
            {
                case NPC_BLACKWING_DRAGON:
                case NPC_BLACKWING_TASKMASTER:
                case NPC_BLACKWING_LEGIONAIRE:
                case NPC_BLACKWING_WARLOCK:
                    if (Creature* razor = instance->GetCreature(GetGuidData(DATA_RAZORGORE_THE_UNTAMED)))
                        if (CreatureAI* razorAI = razor->AI())
                            razorAI->JustSummoned(creature);
                    break;
                default:
                    break;
            }
        }

        void OnGameObjectCreate(GameObject* go) override
        {
            InstanceScript::OnGameObjectCreate(go);

            switch(go->GetEntry())
            {
                case GO_BLACK_DRAGON_EGG:
                    if (GetBossState(DATA_FIREMAW) == DONE)
                        go->SetPhaseMask(2, true);
                    else
                        EggList.push_back(go->GetGUID());
                    break;

                case GO_PORTCULLIS_RAZORGORE:
                case GO_PORTCULLIS_VAELASTRASZ:
                case GO_PORTCULLIS_BROODLORD:
                case GO_PORTCULLIS_THREEDRAGONS:
                case GO_PORTCULLIS_CHROMAGGUS:
                case GO_PORTCULLIS_NEFARIAN:
                    AddDoor(go, true);
                    break;
                default:
                    break;
            }
        }

        void OnGameObjectRemove(GameObject* go) override
        {
            InstanceScript::OnGameObjectRemove(go);

            if (go->GetEntry() == GO_BLACK_DRAGON_EGG)
                EggList.remove(go->GetGUID());

            switch (go->GetEntry())
            {
            case GO_PORTCULLIS_RAZORGORE:
            case GO_PORTCULLIS_VAELASTRASZ:
            case GO_PORTCULLIS_BROODLORD:
            case GO_PORTCULLIS_THREEDRAGONS:
            case GO_PORTCULLIS_CHROMAGGUS:
            case GO_PORTCULLIS_NEFARIAN:
                AddDoor(go, false);
                break;
            default:
                break;
            }
        }

        bool CheckRequiredBosses(uint32 bossId, Player const* /* player */) const override
        {
            switch (bossId)
            {
                case DATA_BROODLORD_LASHLAYER:
                    if (GetBossState(DATA_VAELASTRAZ_THE_CORRUPT) != DONE)
                        return false;
                    break;
                case DATA_CHROMAGGUS:
                    if (GetBossState(DATA_FIREMAW) != DONE
                        || GetBossState(DATA_EBONROC) != DONE
                        || GetBossState(DATA_FLAMEGOR) != DONE)
                        return false;
                    break;
                default:
                    break;
            }

            return true;
        }

        bool SetBossState(uint32 type, EncounterState state) override
        {
            if (!InstanceScript::SetBossState(type, state))
                return false;

            switch (type)
            {
                case DATA_RAZORGORE_THE_UNTAMED:
                    if (state == DONE)
                    {
                        for (ObjectGuid const& guid : EggList)
                            if (GameObject* egg = instance->GetGameObject(guid))
                                egg->SetPhaseMask(2, true);
                    }
                    SetData(DATA_EGG_EVENT, NOT_STARTED);
                    break;
                case DATA_NEFARIAN:
                    switch (state)
                    {
                        case NOT_STARTED:
                            if (Creature* nefarian = instance->GetCreature(GetGuidData(DATA_NEFARIAN)))
                                nefarian->DespawnOrUnsummon();
                            break;
                        case FAIL:
                            _events.ScheduleEvent(EVENT_RESPAWN_NEFARIUS, 15 * 60 * IN_MILLISECONDS); //15min
                            SetBossState(DATA_NEFARIAN, NOT_STARTED);
                            break;
                        default:
                            break;
                    }
                    break;
            }
            return true;
        }

        void SetData(uint32 type, uint32 data) override
        {
            if (type == DATA_EGG_EVENT)
            {
                switch (data)
                {
                    case IN_PROGRESS:
                        _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 45000);
                        EggEvent = data;
                        EggCount = 0;
                        break;
                    case NOT_STARTED:
                        _events.CancelEvent(EVENT_RAZOR_SPAWN);
                        EggEvent = data;
                        EggCount = 0;
                        break;
                    case SPECIAL:
                        if (++EggCount == 15)
                        {
                            if (Creature* razor = instance->GetCreature(GetGuidData(DATA_RAZORGORE_THE_UNTAMED)))
                            {
                                SetData(DATA_EGG_EVENT, DONE);
                                razor->RemoveAurasDueToSpell(42013); // MindControl
                                DoRemoveAurasDueToSpellOnPlayers(42013);
                            }
                            _events.ScheduleEvent(EVENT_RAZOR_PHASE_TWO, 1000);
                            _events.CancelEvent(EVENT_RAZOR_SPAWN);
                        }
                        if (EggEvent == NOT_STARTED)
                            SetData(DATA_EGG_EVENT, IN_PROGRESS);
                        break;
                }
            }
        }

        void OnUnitDeath(Unit* unit) override
        {
            //! HACK, needed because of buggy CreatureAI after charm
            if (unit->GetEntry() == NPC_RAZORGORE && GetBossState(DATA_RAZORGORE_THE_UNTAMED) != DONE)
                SetBossState(DATA_RAZORGORE_THE_UNTAMED, DONE);
        }

        void Update(uint32 diff) override
        {
            if (_events.Empty())
                return;

            _events.Update(diff);

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_RAZOR_SPAWN:
                        for (uint8 i = urand(2, 5); i > 0; --i)
                            if (Creature* summon = instance->SummonCreature(Entry[urand(0, 4)], SummonPosition[urand(0, 7)]))
                                summon->AI()->DoZoneInCombat();
                        _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 12000, 17000);
                        break;
                    case EVENT_RAZOR_PHASE_TWO:
                        _events.CancelEvent(EVENT_RAZOR_SPAWN);
                        if (Creature* razor = instance->GetCreature(GetGuidData(DATA_RAZORGORE_THE_UNTAMED)))
                            razor->AI()->DoAction(ACTION_PHASE_TWO);
                        break;
                    case EVENT_RESPAWN_NEFARIUS:
                        if (Creature* nefarius = instance->GetCreature(GetGuidData(DATA_LORD_VICTOR_NEFARIUS)))
                        {
                            nefarius->SetPhaseMask(1, true);
                            nefarius->setActive(true);
                            nefarius->Respawn();
                            nefarius->GetMotionMaster()->MoveTargetedHome();
                        }
                        break;
                }
            }
        }

    protected:
        // Misc
        EventMap _events;

        // Razorgore
        uint8 EggCount;
        uint32 EggEvent;
        GuidList EggList;
    };

    InstanceScript* GetInstanceScript(InstanceMap* map) const
    {
        return new instance_blackwing_lair_InstanceMapScript(map);
    }
};

enum ShadowFlame
{
    SPELL_ONYXIA_SCALE_CLOAK = 22683,
    SPELL_SHADOW_FLAME_DOT = 22682
};

// 22539 - Shadowflame (used in Blackwing Lair)
class spell_bwl_shadowflame : public SpellScriptLoader
{
public:
    spell_bwl_shadowflame() : SpellScriptLoader("spell_bwl_shadowflame") { }

    class spell_bwl_shadowflame_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_bwl_shadowflame_SpellScript);

        bool Validate(SpellInfo const* /*spellInfo*/) override
        {
            return ValidateSpellInfo({ SPELL_ONYXIA_SCALE_CLOAK, SPELL_SHADOW_FLAME_DOT });
        }

        void HandleEffectScriptEffect(SpellEffIndex /*effIndex*/)
        {
            // If the victim of the spell does not have "Onyxia Scale Cloak" - add the Shadow Flame DoT (22682)
            if (Unit* victim = GetHitUnit())
                if (!victim->HasAura(SPELL_ONYXIA_SCALE_CLOAK))
                    victim->AddAura(SPELL_SHADOW_FLAME_DOT, victim);
        }

        void Register() override
        {
            OnEffectHitTarget += SpellEffectFn(spell_bwl_shadowflame_SpellScript::HandleEffectScriptEffect, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
        }
    };

    SpellScript* GetSpellScript() const
    {
        return new spell_bwl_shadowflame_SpellScript;
    }
};

void AddSC_instance_blackwing_lair()
{
    new instance_blackwing_lair();
    new spell_bwl_shadowflame();
}
<commit_msg>fix(Scripts/BlackwingLair): starting Razorgore the Untamed fight (#7783)<commit_after>/*
 * Copyright (C) 2016+     AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
 * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 */

#include "blackwing_lair.h"
#include "GameObject.h"
#include "InstanceScript.h"
#include "Map.h"
#include "MotionMaster.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "TemporarySummon.h"

DoorData const doorData[] =
{
    { GO_PORTCULLIS_RAZORGORE,    DATA_RAZORGORE_THE_UNTAMED,  DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 175946 || GUID 7230
    { GO_PORTCULLIS_VAELASTRASZ,  DATA_VAELASTRAZ_THE_CORRUPT, DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 175185 || GUID 7229
    { GO_PORTCULLIS_BROODLORD,    DATA_BROODLORD_LASHLAYER,    DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179365 || GUID 75159
    { GO_PORTCULLIS_THREEDRAGONS, DATA_FIREMAW,                DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179115 || GUID 75165
    { GO_PORTCULLIS_THREEDRAGONS, DATA_EBONROC,                DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179115 || GUID 75165
    { GO_PORTCULLIS_THREEDRAGONS, DATA_FLAMEGOR,               DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179115 || GUID 75165
    { GO_PORTCULLIS_CHROMAGGUS,   DATA_CHROMAGGUS,             DOOR_TYPE_PASSAGE, BOUNDARY_NONE}, // ID 179116 || GUID 75161
    { GO_PORTCULLIS_NEFARIAN,     DATA_NEFARIAN,               DOOR_TYPE_ROOM, BOUNDARY_NONE},    // ID 179117 || GUID 75164
    { 0,                         0,                            DOOR_TYPE_ROOM, BOUNDARY_NONE}     // END
};

Position const SummonPosition[8] =
{
    {-7661.207520f, -1043.268188f, 407.199554f, 6.280452f},
    {-7644.145020f, -1065.628052f, 407.204956f, 0.501492f},
    {-7624.260742f, -1095.196899f, 407.205017f, 0.544694f},
    {-7608.501953f, -1116.077271f, 407.199921f, 0.816443f},
    {-7531.841797f, -1063.765381f, 407.199615f, 2.874187f},
    {-7547.319336f, -1040.971924f, 407.205078f, 3.789175f},
    {-7568.547852f, -1013.112488f, 407.204926f, 3.773467f},
    {-7584.175781f, -989.6691289f, 407.199585f, 4.527447f},
};

uint32 const Entry[5] = {12422, 12458, 12416, 12420, 12459};

class instance_blackwing_lair : public InstanceMapScript
{
public:
    instance_blackwing_lair() : InstanceMapScript(BWLScriptName, 469) { }

    struct instance_blackwing_lair_InstanceMapScript : public InstanceScript
    {
        instance_blackwing_lair_InstanceMapScript(Map* map) : InstanceScript(map)
        {
            //SetHeaders(DataHeader);
            SetBossNumber(EncounterCount);
            LoadDoorData(doorData);
            //LoadObjectData(creatureData, gameObjectData);
        }

        void Initialize() override
        {
            // Razorgore
            EggCount = 0;
            EggEvent = 0;
        }

        void OnCreatureCreate(Creature* creature) override
        {
            InstanceScript::OnCreatureCreate(creature);

            switch (creature->GetEntry())
            {
                case NPC_RAZORGORE:
                    razorgoreGUID = creature->GetGUID();
                    break;
                case NPC_BLACKWING_DRAGON:
                case NPC_BLACKWING_TASKMASTER:
                case NPC_BLACKWING_LEGIONAIRE:
                case NPC_BLACKWING_WARLOCK:
                    if (Creature* razor = instance->GetCreature(razorgoreGUID))
                        if (CreatureAI* razorAI = razor->AI())
                            razorAI->JustSummoned(creature);
                    break;
                default:
                    break;
            }
        }

        void OnGameObjectCreate(GameObject* go) override
        {
            InstanceScript::OnGameObjectCreate(go);

            switch(go->GetEntry())
            {
                case GO_BLACK_DRAGON_EGG:
                    if (GetBossState(DATA_FIREMAW) == DONE)
                        go->SetPhaseMask(2, true);
                    else
                        EggList.push_back(go->GetGUID());
                    break;

                case GO_PORTCULLIS_RAZORGORE:
                case GO_PORTCULLIS_VAELASTRASZ:
                case GO_PORTCULLIS_BROODLORD:
                case GO_PORTCULLIS_THREEDRAGONS:
                case GO_PORTCULLIS_CHROMAGGUS:
                case GO_PORTCULLIS_NEFARIAN:
                    AddDoor(go, true);
                    break;
                default:
                    break;
            }
        }

        void OnGameObjectRemove(GameObject* go) override
        {
            InstanceScript::OnGameObjectRemove(go);

            if (go->GetEntry() == GO_BLACK_DRAGON_EGG)
                EggList.remove(go->GetGUID());

            switch (go->GetEntry())
            {
            case GO_PORTCULLIS_RAZORGORE:
            case GO_PORTCULLIS_VAELASTRASZ:
            case GO_PORTCULLIS_BROODLORD:
            case GO_PORTCULLIS_THREEDRAGONS:
            case GO_PORTCULLIS_CHROMAGGUS:
            case GO_PORTCULLIS_NEFARIAN:
                AddDoor(go, false);
                break;
            default:
                break;
            }
        }

        bool CheckRequiredBosses(uint32 bossId, Player const* /* player */) const override
        {
            switch (bossId)
            {
                case DATA_BROODLORD_LASHLAYER:
                    if (GetBossState(DATA_VAELASTRAZ_THE_CORRUPT) != DONE)
                        return false;
                    break;
                case DATA_CHROMAGGUS:
                    if (GetBossState(DATA_FIREMAW) != DONE
                        || GetBossState(DATA_EBONROC) != DONE
                        || GetBossState(DATA_FLAMEGOR) != DONE)
                        return false;
                    break;
                default:
                    break;
            }

            return true;
        }

        bool SetBossState(uint32 type, EncounterState state) override
        {
            if (!InstanceScript::SetBossState(type, state))
                return false;

            switch (type)
            {
                case DATA_RAZORGORE_THE_UNTAMED:
                    if (state == DONE)
                    {
                        for (ObjectGuid const& guid : EggList)
                            if (GameObject* egg = instance->GetGameObject(guid))
                                egg->SetPhaseMask(2, true);
                    }
                    SetData(DATA_EGG_EVENT, NOT_STARTED);
                    break;
                case DATA_NEFARIAN:
                    switch (state)
                    {
                        case NOT_STARTED:
                            if (Creature* nefarian = instance->GetCreature(GetGuidData(DATA_NEFARIAN)))
                                nefarian->DespawnOrUnsummon();
                            break;
                        case FAIL:
                            _events.ScheduleEvent(EVENT_RESPAWN_NEFARIUS, 15 * 60 * IN_MILLISECONDS); //15min
                            SetBossState(DATA_NEFARIAN, NOT_STARTED);
                            break;
                        default:
                            break;
                    }
                    break;
            }
            return true;
        }

        void SetData(uint32 type, uint32 data) override
        {
            if (type == DATA_EGG_EVENT)
            {
                switch (data)
                {
                    case IN_PROGRESS:
                        _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 45000);
                        EggEvent = data;
                        EggCount = 0;
                        break;
                    case NOT_STARTED:
                        _events.CancelEvent(EVENT_RAZOR_SPAWN);
                        EggEvent = data;
                        EggCount = 0;
                        break;
                    case SPECIAL:
                        if (++EggCount == 15)
                        {
                            if (Creature* razor = instance->GetCreature(razorgoreGUID))
                            {
                                SetData(DATA_EGG_EVENT, DONE);
                                razor->RemoveAurasDueToSpell(42013); // MindControl
                                DoRemoveAurasDueToSpellOnPlayers(42013);
                            }
                            _events.ScheduleEvent(EVENT_RAZOR_PHASE_TWO, 1000);
                            _events.CancelEvent(EVENT_RAZOR_SPAWN);
                        }
                        if (EggEvent == NOT_STARTED)
                            SetData(DATA_EGG_EVENT, IN_PROGRESS);
                        break;
                }
            }
        }

        ObjectGuid GetGuidData(uint32 type) const override
        {
            switch (type)
            {
                case DATA_RAZORGORE_THE_UNTAMED:
                    return razorgoreGUID;
                default:
                    break;
            }

            return ObjectGuid::Empty;
        }

        void OnUnitDeath(Unit* unit) override
        {
            //! HACK, needed because of buggy CreatureAI after charm
            if (unit->GetEntry() == NPC_RAZORGORE && GetBossState(DATA_RAZORGORE_THE_UNTAMED) != DONE)
                SetBossState(DATA_RAZORGORE_THE_UNTAMED, DONE);
        }

        void Update(uint32 diff) override
        {
            if (_events.Empty())
                return;

            _events.Update(diff);

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_RAZOR_SPAWN:
                        for (uint8 i = urand(2, 5); i > 0; --i)
                            if (Creature* summon = instance->SummonCreature(Entry[urand(0, 4)], SummonPosition[urand(0, 7)]))
                                summon->AI()->DoZoneInCombat();
                        _events.ScheduleEvent(EVENT_RAZOR_SPAWN, 12000, 17000);
                        break;
                    case EVENT_RAZOR_PHASE_TWO:
                        _events.CancelEvent(EVENT_RAZOR_SPAWN);
                        if (Creature* razor = instance->GetCreature(razorgoreGUID))
                            razor->AI()->DoAction(ACTION_PHASE_TWO);
                        break;
                    case EVENT_RESPAWN_NEFARIUS:
                        if (Creature* nefarius = instance->GetCreature(GetGuidData(DATA_LORD_VICTOR_NEFARIUS)))
                        {
                            nefarius->SetPhaseMask(1, true);
                            nefarius->setActive(true);
                            nefarius->Respawn();
                            nefarius->GetMotionMaster()->MoveTargetedHome();
                        }
                        break;
                }
            }
        }

    protected:
        // Misc
        EventMap _events;

        // Razorgore
        ObjectGuid razorgoreGUID;
        uint8 EggCount;
        uint32 EggEvent;
        GuidList EggList;
    };

    InstanceScript* GetInstanceScript(InstanceMap* map) const
    {
        return new instance_blackwing_lair_InstanceMapScript(map);
    }
};

enum ShadowFlame
{
    SPELL_ONYXIA_SCALE_CLOAK = 22683,
    SPELL_SHADOW_FLAME_DOT = 22682
};

// 22539 - Shadowflame (used in Blackwing Lair)
class spell_bwl_shadowflame : public SpellScriptLoader
{
public:
    spell_bwl_shadowflame() : SpellScriptLoader("spell_bwl_shadowflame") { }

    class spell_bwl_shadowflame_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_bwl_shadowflame_SpellScript);

        bool Validate(SpellInfo const* /*spellInfo*/) override
        {
            return ValidateSpellInfo({ SPELL_ONYXIA_SCALE_CLOAK, SPELL_SHADOW_FLAME_DOT });
        }

        void HandleEffectScriptEffect(SpellEffIndex /*effIndex*/)
        {
            // If the victim of the spell does not have "Onyxia Scale Cloak" - add the Shadow Flame DoT (22682)
            if (Unit* victim = GetHitUnit())
                if (!victim->HasAura(SPELL_ONYXIA_SCALE_CLOAK))
                    victim->AddAura(SPELL_SHADOW_FLAME_DOT, victim);
        }

        void Register() override
        {
            OnEffectHitTarget += SpellEffectFn(spell_bwl_shadowflame_SpellScript::HandleEffectScriptEffect, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
        }
    };

    SpellScript* GetSpellScript() const
    {
        return new spell_bwl_shadowflame_SpellScript;
    }
};

void AddSC_instance_blackwing_lair()
{
    new instance_blackwing_lair();
    new spell_bwl_shadowflame();
}
<|endoftext|>