Skip to content

Commit

Permalink
Added effects "Majority Voting" and "Minority Voting"
Browse files Browse the repository at this point in the history
  • Loading branch information
Regynate committed Jan 23, 2025
1 parent 51b4994 commit dc566a5
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 12 deletions.
8 changes: 4 additions & 4 deletions ChaosMod/Components/EffectDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,13 +409,13 @@ void EffectDispatcher::UpdateMetaEffects(int deltaTime)
std::vector<std::tuple<EffectIdentifier, EffectData *>> availableMetaEffects;

float totalWeight = 0.f;
for (auto &[effectId, effectData] : g_EnabledEffects)
for (auto effectData : GetFilteredEnabledEffects())
{
if (effectData.IsMeta() && !effectData.IsUtility() && !effectData.IsHidden())
if (effectData->IsMeta() && !effectData->IsUtility() && !effectData->IsHidden())
{
totalWeight += effectData.GetEffectWeight();
totalWeight += effectData->GetEffectWeight();

availableMetaEffects.push_back(std::make_tuple(effectId, &effectData));
availableMetaEffects.push_back(std::make_tuple(effectData->Id, effectData));
}
}

Expand Down
2 changes: 2 additions & 0 deletions ChaosMod/Components/MetaModifiers.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "Components/Component.h"
#include "Util/VotingMode.h"

#include <cstdint>

Expand All @@ -13,4 +14,5 @@ class MetaModifiers : public Component
bool HideChaosUI = false;
bool DisableChaos = false;
bool FlipChaosUI = false;
VotingMode VotingModeOverride = VotingMode::None;
};
31 changes: 26 additions & 5 deletions ChaosMod/Components/Voting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ bool Voting::Init()
m_OverlayMode = g_OptionsManager.GetVotingValue({ "VotingOverlayMode", "TwitchVotingOverlayMode" },
static_cast<OverlayMode>(OPTION_DEFAULT_TWITCH_OVERLAY_MODE));

m_EnableChanceSystem = g_OptionsManager.GetVotingValue({ "VotingChanceSystem", "TwitchVotingChanceSystem" },
OPTION_DEFAULT_TWITCH_PROPORTIONAL_VOTING);
m_VotingMode = g_OptionsManager.GetVotingValue({ "VotingChanceSystem", "TwitchVotingChanceSystem" },
OPTION_DEFAULT_TWITCH_PROPORTIONAL_VOTING) ? VotingMode::Percentage : VotingMode::Majority;
m_EnableVotingChanceSystemRetainInitialChance =
g_OptionsManager.GetVotingValue({ "VotingChanceSystemRetainChance", "TwitchVotingChanceSystemRetainChance" },
OPTION_DEFAULT_TWITCH_PROPORTIONAL_VOTING_RETAIN_CHANCE);
Expand Down Expand Up @@ -92,6 +92,11 @@ bool Voting::IsEnabled() const
return m_EnableVoting;
}

VotingMode Voting::GetVotingMode() const
{
return m_VotingMode;
}

void Voting::HandleMsg(std::string_view message)
{
if (message == "hello")
Expand Down Expand Up @@ -259,7 +264,7 @@ void Voting::OnRun()
{
m_LastVotesFetchTime = curTick;

if (m_IsVotingRunning && m_EnableChanceSystem && m_OverlayMode == OverlayMode::OverlayIngame)
if (m_IsVotingRunning && m_VotingMode == VotingMode::Percentage && m_OverlayMode == OverlayMode::OverlayIngame)
{
// Get current vote status to display procentages on screen
SendToPipe("getcurrentvotes");
Expand All @@ -278,6 +283,22 @@ void Voting::OnRun()
if (!m_ReceivedHello)
return;

if (ComponentExists<MetaModifiers>())
{
auto newMode = GetComponent<MetaModifiers>()->VotingModeOverride;
if (m_VotingModeOverride != newMode)
{
m_VotingModeOverride = newMode;

if (newMode == VotingMode::None)
{
newMode = m_VotingMode;
}

SendToPipe(std::string("votingmode"), { newMode.ToString() });
}
}

if (GetComponent<EffectDispatchTimer>()->GetRemainingTimerTime() <= 1 && !m_HasReceivedResult)
{
// Get vote result 1 second before effect is supposed to dispatch
Expand Down Expand Up @@ -398,7 +419,7 @@ void Voting::OnRun()

// Count total votes if chance system is enabled
int totalVotes = 0;
if (m_EnableChanceSystem)
if (m_VotingMode == VotingMode::Percentage)
{
for (const auto &choosableEffect : m_EffectChoices)
{
Expand All @@ -416,7 +437,7 @@ void Voting::OnRun()
oss << choosableEffect->Match << ": " << choosableEffect->Name;

// Also show chance percentages if chance system is enabled
if (m_EnableChanceSystem)
if (m_VotingMode == VotingMode::Percentage)
{
float percentage;
if (totalVotes == 0)
Expand Down
6 changes: 5 additions & 1 deletion ChaosMod/Components/Voting.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "Components/Component.h"
#include "Util/VotingMode.h"

#include <cstdint>
#include <memory>
Expand Down Expand Up @@ -53,17 +54,20 @@ class Voting : public Component
bool m_IsVotingRoundDone = true;
bool m_AlternatedVotingRound = false;

bool m_EnableChanceSystem = false;
VotingMode m_VotingMode = VotingMode::Majority;
bool m_EnableVotingChanceSystemRetainInitialChance = true;
bool m_EnableRandomEffectVoteable = true;

bool m_IsVotingRunning = false;

VotingMode m_VotingModeOverride = VotingMode::None;

public:
Voting(const std::array<std::uint8_t, 3> &TextColor);

bool Init();
bool IsEnabled() const;
VotingMode GetVotingMode() const;
void HandleMsg(std::string_view message);

private:
Expand Down
11 changes: 11 additions & 0 deletions ChaosMod/Effects/Condition/ConditionProportionalVotingEnabled.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <stdafx.h>

#include "Components/Voting.h"
#include "Effects/Condition/EffectCondition.h"

static bool OnCondition()
{
return ComponentExists<Voting>() && GetComponent<Voting>()->IsEnabled() && GetComponent<Voting>()->GetVotingMode() == VotingMode::Percentage;
}

REGISTER_EFFECT_CONDITION(EffectConditionType::ProportionalVotingEnabled, OnCondition);
2 changes: 1 addition & 1 deletion ChaosMod/Effects/Condition/ConditionVotingEnabled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

static bool OnCondition()
{
return !ComponentExists<Voting>() || GetComponent<Voting>()->IsEnabled();
return ComponentExists<Voting>() && GetComponent<Voting>()->IsEnabled();
}

REGISTER_EFFECT_CONDITION(EffectConditionType::VotingEnabled, OnCondition);
1 change: 1 addition & 0 deletions ChaosMod/Effects/Condition/EffectCondition.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ enum class EffectConditionType
{
None,
VotingEnabled, // Voting is enabled
ProportionalVotingEnabled
};

#define REGISTER_EFFECT_CONDITION(conditionType, condition) \
Expand Down
49 changes: 49 additions & 0 deletions ChaosMod/Effects/db/Meta/MetaVotingMode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Effects by Regynate
*/

#include <stdafx.h>

#include "Components/MetaModifiers.h"
#include "Effects/Register/RegisterEffect.h"

static void OnStop()
{
GetComponent<MetaModifiers>()->VotingModeOverride = VotingMode::None;
}

static void OnStart_Majority()
{
GetComponent<MetaModifiers>()->VotingModeOverride = VotingMode::Majority;
}

// clang-format off
REGISTER_EFFECT(OnStart_Majority, OnStop, nullptr,
{
.Name = "Majority Voting",
.Id = "meta_votingmode_majority",
.IsTimed = true,
.IncompatibleWith = { "meta_votingmode_antimajority" },
.ExecutionType = EffectExecutionType::Meta,
.ConditionType = EffectConditionType::ProportionalVotingEnabled
}
);
// clang-format on

static void OnStart_Antimajority()
{
GetComponent<MetaModifiers>()->VotingModeOverride = VotingMode::Antimajority;
}

// clang-format off
REGISTER_EFFECT(OnStart_Antimajority, OnStop, nullptr,
{
.Name = "Minority Voting",
.Id = "meta_votingmode_antimajority",
.IsTimed = true,
.IncompatibleWith = { "meta_votingmode_majority" },
.ExecutionType = EffectExecutionType::Meta,
.ConditionType = EffectConditionType::VotingEnabled
}
);
// clang-format on
50 changes: 50 additions & 0 deletions ChaosMod/Util/VotingMode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once

#include <string>

class VotingMode
{
public:
enum Value
{
None,
Majority,
Percentage,
Antimajority
};

VotingMode() : value(None)
{
}

VotingMode(Value mode) : value(mode)
{
}

bool operator==(VotingMode a) const
{
return value == a.value;
}
bool operator!=(VotingMode a) const
{
return value != a.value;
}

std::string ToString()
{
switch (value)
{
case VotingMode::None:
return "NONE";
case VotingMode::Majority:
return "MAJORITY";
case VotingMode::Percentage:
return "PERCENTAGE";
case VotingMode::Antimajority:
return "ANTIMAJORITY";
}
}

private:
Value value;
};
2 changes: 2 additions & 0 deletions ConfigApp/Effects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ public enum EffectTimedType
{ "screen_hueshift", new EffectInfo("Hue Shift", EffectCategory.Screen, true) },
{ "player_copyforce", new EffectInfo("Use The Force", EffectCategory.Player, true, true) },
{ "player_tptowaypointopposite", new EffectInfo("Teleport To The Opposite Side Of Waypoint", EffectCategory.Player) },
{ "meta_votingmode_majority", new EffectInfo("Majority Voting", EffectCategory.Meta, true) },
{ "meta_votingmode_antimajority", new EffectInfo("Minority Voting", EffectCategory.Meta, true) },
};
}
}
30 changes: 29 additions & 1 deletion TwitchChatVotingProxy/ChaosModController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Timers;
using System;
using System.Timers;
using Serilog;
using TwitchChatVotingProxy.ChaosPipe;
using TwitchChatVotingProxy.OverlayServer;
Expand Down Expand Up @@ -36,6 +37,7 @@ public ChaosModController(IChaosPipeClient chaosPipe, IOverlayServer? overlaySer
m_ChaosPipe.OnGetVoteResult += OnGetVoteResult;
m_ChaosPipe.OnNewVote += OnNewVote;
m_ChaosPipe.OnNoVotingRound += OnNoVotingRound;
m_ChaosPipe.OnSetVotingMode += OnSetVotingMode;

// Setup receiver listeners
foreach (var votingReceiver in m_VotingReceivers)
Expand Down Expand Up @@ -75,6 +77,7 @@ private int GetVoteResultByMajority()
/// <summary>
/// Calculate the voting result by assigning them a percentage based on votes,
/// and choosing a random option based on that percentage.
/// </summary>
private int GetVoteResultByPercentage()
{
// Get total votes
Expand Down Expand Up @@ -103,6 +106,28 @@ private int GetVoteResultByPercentage()
return selectedOption;
}
/// <summary>
/// Calculate the voting result by the option with the lowest votes
/// </summary>
private int GetVoteResultByAntiMajority()
{
var lowestVoteCount = m_ActiveVoteOptions.Min(_ => _.Votes);
var chosenOptions = m_ActiveVoteOptions.FindAll(_ => _.Votes == lowestVoteCount);
IVoteOption chosenOption;
if (chosenOptions.Count == 1)
{
chosenOption = chosenOptions[0];
}
else
{
chosenOption = chosenOptions[m_Random.Next(0, chosenOptions.Count)];
}
return m_ActiveVoteOptions.IndexOf(chosenOption);
}
public void OnSetVotingMode(object? sender, OnSetVotingModeArgs onSetVotingModeArgs)
{
m_Config.VotingMode = onSetVotingModeArgs.VotingMode;
}
/// <summary>
/// Is called when the chaos mod pipe requests the current votes (callback)
/// </summary>
private void OnGetCurrentVotes(object? sender, OnGetCurrentVotesArgs args)
Expand Down Expand Up @@ -133,6 +158,9 @@ private void OnGetVoteResult(object? sender, OnGetVoteResultArgs e)
case EVotingMode.PERCENTAGE:
e.ChosenOption = GetVoteResultByPercentage();
break;
case EVotingMode.ANTIMAJORITY:
e.ChosenOption = GetVoteResultByAntiMajority();
break;
}

// Vote round ended
Expand Down
21 changes: 21 additions & 0 deletions TwitchChatVotingProxy/ChaosPipe/ChaosPipeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ChaosPipeClient : IChaosPipeClient
public event EventHandler<OnGetCurrentVotesArgs>? OnGetCurrentVotes = null;
public event EventHandler<OnGetVoteResultArgs>? OnGetVoteResult = null;
public event EventHandler<OnNewVoteArgs>? OnNewVote = null;
public event EventHandler<OnSetVotingModeArgs>? OnSetVotingMode = null;
public event EventHandler? OnNoVotingRound = null;

private readonly ILogger m_Logger = Log.Logger.ForContext<ChaosPipeClient>();
Expand Down Expand Up @@ -202,6 +203,9 @@ private void ReadPipe()
case "vote":
StartNewVote(pipe.Options);
break;
case "votingmode":
ChangeVotingMode(pipe.Options);
break;
case "getvoteresult":
GetVoteResult();
break;
Expand Down Expand Up @@ -239,6 +243,23 @@ private void StartNewVote(List<string>? options)
// Dispatch information to listeners
OnNewVote?.Invoke(this, new OnNewVoteArgs(options.ToArray()));
}
private void ChangeVotingMode(List<string>? options)
{
if (options == null || options.Count == 0)
return;

string modeName = options[0];

EVotingMode? mode = VotingMode.ReverseLookup(modeName);
if (mode == null)
{
m_Logger.Error("Unknown voting mode: " + modeName);
return;
}

m_Logger.Information("Setting voting mode to " + modeName);
OnSetVotingMode?.Invoke(this, new((EVotingMode)mode));
}
/// <summary>
/// Start a no-voting round. The chaos mod will decide over the options
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions TwitchChatVotingProxy/ChaosPipe/IChaosPipeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface IChaosPipeClient
/// Event that gets invoked when the chaos mod creates a new vote
/// </summary>
event EventHandler<OnNewVoteArgs> OnNewVote;
event EventHandler<OnSetVotingModeArgs> OnSetVotingMode;
/// <summary>
/// Event that gets invoked when the chaos mod starts a no voting round
/// </summary>
Expand Down
Loading

0 comments on commit dc566a5

Please sign in to comment.