Skip to content
Wout Gryseels
C++ / Dx11

The Alchemist

Game project in C++ / DirectX11 using the Overlord Engine. Top-down single player game made in four weeks.

The Alchemist screenshot 1
The Alchemist screenshot 2
The Alchemist screenshot 3
The Alchemist screenshot 4
quiver.cpp
void Quiver::Initialize(const GameContext& gameContext)
{

    for (UINT i = 0; i < m_AmountOfArrows; i++)
    {
        auto  arrow = new Arrow();
        m_pArrowsVec.push_back(arrow);
        AddChild(arrow);
    }
    m_pHealingPotion = new HealingPotion;
    AddChild(m_pHealingPotion);

    m_PanicBolt = new PanicBolt; 
    AddChild(m_PanicBolt);

    m_ClarityPotion = new ClarityPotion; 
    AddChild(m_ClarityPotion);

    m_ToxicPool = new ToxicPool; 
    AddChild(m_ToxicPool); 

}

void Quiver::Update(const GameContext & gc)
{
    for  (auto & skill : m_SkillsData)
    {
        auto & canCast = std::get<0>(skill); 
        if (!canCast)
        {
            auto & totalCdTime = std::get<1>(skill); 
            auto & currentCd = std::get<2>(skill); 
            if (currentCd > totalCdTime)
            {
                currentCd = 0; 
                canCast = true;
            }
            else
            {
                currentCd += gc.pGameTime->GetElapsed();
            }
        }
    }
}



void Quiver::FireArrow(const DirectX::XMFLOAT3 & pos, float yrot)
{
    //std::cout << "Fire arrow" << std::endl; 
    auto & canCast = std::get<0>(m_SkillsData[0]);
    if (!canCast)
    {
        return;
    }
    std::list<Arrow *>::iterator arrowIt;
    for  (arrowIt = m_pArrowsVec.begin(); arrowIt != m_pArrowsVec.end() ; ++arrowIt)
    {
        auto & arrow = *arrowIt; 
        if  (!arrow->m_IsFlying )// && ! arrow->m_IsStuck)
        {
            arrow->Launch(pos, yrot);
            m_pArrowsVec.emplace_back(arrow);
            m_pArrowsVec.erase(arrowIt);
            canCast = false;
            return;
        }
    }

}

quiver.h
class Quiver : public GameObject 
{
public:
    //A quiver is a container for holding different abilities (arrows & vials) 
    Quiver();
    virtual ~Quiver() = default;
    Quiver(const Quiver& other) = delete;
    Quiver(Quiver&& other) noexcept = delete;
    Quiver& operator=(const Quiver& other) = delete;
    Quiver& operator=(Quiver&& other) noexcept = delete;
    void Initialize(const GameContext& gameContext) override;
    void PostInitialize(const GameContext& gameContext) override;
    void Update(const GameContext& gameContext) override;
    void PostDraw(const GameContext & gameContext) override; 
    void FireArrow(const DirectX::XMFLOAT3  & pos, float  yrot);
    void ThrowHealingPot(const DirectX::XMFLOAT3 & charPos, const DirectX::XMFLOAT3 & targetPos);
    void Shield(); 
    void FirePanicBolt(const DirectX::XMFLOAT3  & pos, float  yrot);
    void ThrowClarityPot(const DirectX::XMFLOAT3 & charPos, const DirectX::XMFLOAT3 & targetPos);
    void ThrowToxicPool(const DirectX::XMFLOAT3 & charPos, const DirectX::XMFLOAT3 & targetPos);


    UINT m_AmountOfArrows = 60; 

    std::list<Arrow  *> m_pArrowsVec;
    HealingPotion *  m_pHealingPotion; 
    Character * m_pPlayer; 
    PanicBolt * m_PanicBolt; 
    ClarityPotion * m_ClarityPotion; 
    ToxicPool * m_ToxicPool; 

    //CoolDown list 
    // 0 : Can Cast // 1 : Total Cooldown Time // 2: Current CoolDown time // 3: Energy Required // 4: id // 5:  Name
    std::vector<std::tuple<bool, float, float, float, int, std::string>> m_SkillsData =
            {
                {true, 0.7f, 0.f, 0.f , 0, "ToxicBolt"}, //0
                {true, 3.f,  0.f, 0.f , 1, "HealingPotion"}, //1
                {true, 4.f,  0.f, 0.f , 2, "ClarityPotion"},  //2
                {true, 10.f,  0.f, 0.f , 3,"PanicBolt"}, //3
                {true, 30.f, 0.f, 100.f,4, "ToxicGoo"}, //4
                {true, 1.f,  0.f, 25.f, 5,"InjectionEx"},  //5
                {true, 1.f,  0.f, 25.f, 6,"PetrifyEx"}, //6
                {true, 20.f,  0.f, 0.f, 7,"Shield"} //7
            }; 

protected:
    QuadDrawer * m_pDrawer, *m_pDrawer2, *m_pDrawer3, *m_pDrawer4, *m_pDrawer5,  *m_pDrawer6;
    IconMaterial  * m_pIconMat;
};
AttackAiState
std::pair<bool, AiState*> AttackAiState::Update(const GameContext& gc)
{
    auto enemyT = m_Owner.GetTransform(); 
    auto targetT = m_pTarget->GetTransform(); 
    auto pos = enemyT->GetPosition(); 
    auto targetPos = targetT->GetPosition(); 
    auto dt = gc.pGameTime->GetElapsed();
    auto xvec = (targetPos.x - pos.x); 
    auto yvec = (targetPos.z - pos.z); 
    auto dist = sqrt(xvec * xvec + yvec * yvec); 
    xvec /= dist; 
    yvec /= dist; 
    

    if (m_Owner.m_IsFeared)
    {
        xvec = -xvec;
        yvec = -yvec;
    }
    auto vecToTarget = DirectX::XMFLOAT3(xvec * dt * 15 * m_Owner.m_Move, -1, yvec *  dt * 15 * m_Owner.m_Move);
    if (m_Owner.m_IsSlowed) {
        vecToTarget.x *= 0.5;
        vecToTarget.y *= 0.5;
    }
    auto angle = acos(-yvec);
    if(!m_Owner.m_IsAttacking)
    m_Owner.m_pController->Move(vecToTarget);  
    if (xvec > 0)
        m_Owner.m_pCharacterModel->GetTransform()->Rotate(DirectX::XMFLOAT3(0.0f, -angle, 0.0f), false);
    else
        m_Owner.m_pCharacterModel->GetTransform()->Rotate(DirectX::XMFLOAT3(0.0f, angle, 0.0f), false);
    
    if (dist < 8)
    {
        m_Owner.m_IsAttacking = true; 
        m_Owner.m_pCharacterModel->GetAnimator()->SetAnimation(3);

    }
    return { false, new IdleAiState(m_Owner) };
}
Health shader (HLSL)
// Vertex Shader
VS_OUTPUT VS(VS_INPUT input){
    VS_OUTPUT output;
    output.pos = float4(input.pos, 1.0f);
    output.texCoord = input.texCoord;
    return output;
}

// Pixel Shader
PS_OUTPUT PS(VS_OUTPUT input) : SV_TARGET{

    PS_OUTPUT output = (PS_OUTPUT)0;
    float hpscale = 0.01 * (100-gHealth)  * 0.17;
    if (input.texCoord.y < 0.83 + hpscale)
    {
        return output;
    }
    output.color = 0;
    float res = 1 / 1024.0; 
    float2 tex = input.texCoord;
    tex.x = tex.x * 5.51;//
    tex.y = tex.y * 5.1- 0.017*gTime;
    float4 sam = gSourceMap.Sample(samLinear, tex);
    tex.y = tex.y * 0.61 - 0.014*gTime;
    tex.x += 0.5; 
    float4 sam2 = gSourceMap.Sample(samLinear, tex);
    tex.x = tex.x * 2.3 - 0.025 * gTime;
    tex.y = tex.y * 1.3 - 0.012 * gTime;
    float4 sam3 = gSourceMap.Sample(samLinear, tex);
    tex.x = tex.x * 1.3 + 0.1542 * gTime;
    tex.y = tex.y * 2 - 0.04 * gTime;
    float4 sam4 = gSourceMap.Sample(samLinear,  tex);
    float4 alpha = gAlphaMap.Sample(samLinear, input.texCoord);
    if (sam.a < 0.05)
    {
        discard;
    }
    output.color = lerp(lerp(sam, sam2 +sam3 * 0.5,  0.5), lerp(sam3, sam4, 0.5), 0.5);
    output.color.a = alpha.r + alpha.b  + alpha.g ; 
    output.color.rgb = changeSaturation(output.color.rgb, 7);
    output.color = pow(output.color, 0.75);
    output.color.a =  0.85 * 0.33 *	output.color.a *  (output.color.r+.3);
    return output;
}
float3 changeSaturation(float3 RGB, float change) { 
    //  public-domain function by Darel Rex Finley
    float G;
    float B;
    float R;
    R = RGB.r; 
    G = RGB.g; 
    B = RGB.b; 
    float  Pr = 0.299;
    float  Pg = 0.587;
    float  Pb = 0.114;
    float  P = sqrt(
        (R)*(R)*Pr +
        (G)*(G)*Pg +
        (B)*(B)*Pb);
    R = P + ((R)-P)*change;
    G = P + ((G)-P)*change;
    B = P + ((B)-P)*change;
    return float3(R, G, B);
}
Toxic pool shader (HLSL)
// GEOMETRY SHADER 
void ToxicGenerator(triangle VS_DATA vertices[3], inout TriangleStream<GS_DATA> triStream)
{
    CreateVertex(triStream, vertices[0].Position, vertices[0].Normal, vertices[0].TexCoord * .1 , 1);
    CreateVertex(triStream, vertices[1].Position, vertices[1].Normal, vertices[1].TexCoord * .1 , 1);
    CreateVertex(triStream, vertices[2].Position, vertices[2].Normal, vertices[2].TexCoord * .1 , 1);
    triStream.RestartStrip();
    float pos1 = vertices[0].Position;
    float pos2 = vertices[1].Position;
    float pos3 = vertices[2].Position;
    for (int i = 1; i < 15; i++)
    {
        float f = 1 / i; 
        int c = i * (3 + sin(pow(m_Time,f))); 
        float angle2 = (0.05*(m_Time+i)) * .37;
        angle2 = 0;
        float2x2 uvRotation2 = { cos(angle2), -sin(angle2), sin(angle2), cos(angle2) };
        CreateVertex(triStream, pos1, vertices[0].Normal, (mul(uvRotation2,(vertices[0].TexCoord -0.5))+0.5) * (0.42 - (0.03 * i)), 0.002);
        CreateVertex(triStream, pos2, vertices[1].Normal, (mul(uvRotation2,(vertices[1].TexCoord -0.5))+0.5) * (0.42 - (0.03 * i)), 0.002);
        CreateVertex(triStream, pos3, vertices[2].Normal, (mul(uvRotation2,(vertices[2].TexCoord -0.5))+0.5) * (0.42 - (0.03 * i)), 0.002);
        triStream.RestartStrip();
    }

}
// PIXEL SHADER *
float4 MainPS(GS_DATA input) : SV_TARGET
{

    float x = input.TexCoord.x;
    float y = input.TexCoord.y;
    float distance = sqrt((x * x) + (y * y));
    

    if (distance > 0.25)
    {
        discard;
    }
    float angle =  + m_Time;
    float2x2 uvRotation = { cos(angle), -sin(angle), sin(angle), cos(angle) };
    float2 uv = mul(input.TexCoord, uvRotation);

    float4 r = m_Texture1.Sample(samLinearb, uv + 0.5);
    float4 r12 = m_Texture2.Sample(samLinear, input.TexCoord);
    float angle2 = -m_Time * .37 + 0.8;
    float2x2 uvRotation2 = { cos(angle2), -sin(angle2), sin(angle2), cos(angle2) };
    float2 uv2 = mul(input.TexCoord, uvRotation2);
    float4 r2 = m_Texture1.Sample(samLinearb, uv2 + 0.5);
    float4 r22 = m_Texture2.Sample(samLinear, input.TexCoord + m_Time * 0.1);
    float angle3 = m_Time * .17 + 8.8;
    float2x2 uvRotation3 = { cos(angle3), -sin(angle3), sin(angle3), cos(angle3) };
    float2 uv3 = mul(input.TexCoord, uvRotation3);
    float4 r3 = m_Texture1.Sample(samLinearb, uv3 + 0.5);
    float4 r32 = m_Texture2.Sample(samLinear, input.TexCoord - cos(m_Time * 0.1));
    float angle4 = 0.3* cos(m_Time) + 0.7 * sin(m_Time+.7)  * .27 + 18.8;
    float2x2 uvRotation4 = { cos(angle4), -sin(angle4), sin(angle4), cos(angle4) };
    float2 uv4 = mul(input.TexCoord, uvRotation4);
    float4 r4 = m_Texture1.Sample(samLinearb, uv4 + 0.5);
    float4 r42 = m_Texture2.Sample(samLinear, input.TexCoord + sin(m_Time *0.73));
    r *= 0.9; 
    r2 *= 0.9; 
    r3 *= 0.9; 
    r4 *= 0.9;
    float4 ret = r * sin(r2) * r3 * r4; 
    float4 ret2 = r12 * r22 * r32 * r42; 

    float avg = ret.r + ret.g + ret.b;
    ret.g *= 2.8;
    if (ret.r + ret.g + ret.b < 0.01)
        ret.a -= 1  - (50 *avg); 
    ret.g += 6*avg; 
    ret.b -= 2 * avg;
    ret.r -= .2 * avg;
    if (distance > 0.20)
    {
        ret *= 0.07; 
    }
    if (distance > 0.15)
    {
        ret *= 0.24;
    }
    return pow(ret , 1);
}