I am making an impostor api plugin and the file GameOptionsData.cs has this:
/// <summary>
/// Gets or sets the Impostor cooldown to kill in seconds.
/// </summary>
public float KillCooldown { get; set; }
and I have my own script that has this:
if(message[0] == "/cooldown")
{
if (message[1] != null)
{
int kkd = 0;
bool CanConvert = int.TryParse(message[1], out kkd);
if (CanConvert == true)
{
e.PlayerControl.SendChatToPlayerAsync("you would have made the kill cooldown," + kkd + ", but my code is not currently working Sorry!");
//Set Cooldown here
}
else
{
e.PlayerControl.SendChatToPlayerAsync("This command requires a number \n Example: /cooldown 10");
}
}else
{
e.PlayerControl.SendChatToPlayerAsync("This command requires a number \n Example: /cooldown 10");
}
}
and I want to change the KillCooldown value where it says //Set Cooldown here how would I do this I don't know too much about C# so I apologize if this is a stupid question or I am completely thinking about this in the wrong way. Thanks!
You can simply use:
//set the cool down
e.Game.Options.KillCooldown = 10;
//sync the cooldown to all clients
e.Game.SyncSettingsAsync();
Related
Consider a game in which N players compete for some prize
players are numbered 1 to N, have A1...AN amounts of money and make moves in a circle
player 1 is the first to act
he can either bet amount a1<=A1 or pass the turn to player 2
if player 1 bets, then player 2 can put a bigger bet a2>a1, given that a2<=A2, or pass
the next player can put a bigger bet a3>a2, given that a3<=A3, or pass and so son
if the move returns to player 1, he can add money to his bet so that (a1+added money)>ai where ai is the last bet made, given that a1+added money<=A1
each player is limited to 2 actions per game
The game description consists of
the player list where each item contains the player ID, his position(1...N), and his initial amount of money
the ordered action list where each item contains the player ID, the action(bet/pass), and the amount of the bet if any
Example:
Player List:
PlayerId
Position
Amount
P1
1
60
P2
2
140
P3
3
90
P4
4
200
Action List:
PlayerId
Action
Amount
P1
bet
20
P2
pass
P3
raise
30
P4
raise
40
P1
raise
60
P3
pass
P4
pass
Given the game description as the input, I want to transform it to the
list of N items where each item describes the player's behavior in the game:
PlayerId,
Could Bet First(true if a player had the opportunity to start betting, false otherwise),
Bet First (true if a player started betting, false otherwise)
Could Raise(true if a player had the opportunity to raise the previous bet, false otherwise),
Raised(true if a player had the opportunity to raise the previous bet, false otherwise),
Could Re-raise(true if a player had the opportunity to re-raise, false otherwise),
Re-raised(true if a player bet first, was raised, and re-raised, false otherwise),
Number of times player was active(bet, raised, or reraised)
The total amount of money the player put in the game
The output for the aforementioned example should be:
P1, True, True, False, False, True, True, 2, 60
P2, False, False, False, False, False, False, 0, 0
P3, False, False, True, True, True, False, 1, 30
P4, False, False, True, True, True, False, 1, 40
Ideally, the code would go through the list of actions once and build the desired outcome by updating the player's stat objects with each action. Can anyone suggest an algorithm for solving this problem?
First things first, define a class that matches the desired output data:
class PlayerStatistics
{
public string PlayerId { get; set; }
public bool CouldBetFirst { get; set; }
public bool BetFirst { get; set; }
public bool CouldRaise { get; set; }
public bool Raised { get; set; }
public bool CouldReraise { get; set; }
public bool Reraised { get; set; }
public int NumberOfActions { get; set; }
public decimal AmountPlaced { get; set; }
}
You know how many players there are, and you know there will be one of these statistics objects per player, so you can make a list or array of them.
Now comes the aggregation. You have the list of actions that were taken, but you are also concerned with actions that were possible for your statistics. This means you have to replay the game and keep track of the game state somewhere, so you know if the player actually had enough money remaining to bet, raise etc.
class PlayerState
{
public string PlayerId { get; set; }
public bool Passed { get; set; }
public decimal AmountAvailable { get; set; }
public decimal AmountPlaced { get; set; }
}
// These variables make up the game state
decimal highestAmountPlaced = 0;
int leadingPlayerIndex = -1;
var playerStates = new PlayerState[players.Count];
// Initialize the player states using the info in the list of players
for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
{
playerStates[playerIndex].PlayerId = players[playerIndex].PlayerId;
playerStates[playerIndex].AmountAvailable = players[playerIndex].Amount;
}
Each player can only perform two actions. That means you can simply loop through the list of players 2 times. The loop might exit early if someone wins before then. The key point here is to keep track of the current action, in a variable with a larger scope than the loops, so we can replay the actions in each loop.
int actionIndex = 0;
for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
{
var playerState = states[playerIndex];
if (leadingPlayerIndex == -1)
{
playerState.CouldBetFirst = true;
}
else if (playerState.Available > amountPlaced)
{
playerState.CouldRaise = true;
}
else
{
// They can't raise, but can they match, can they go all-in?
}
var playerAction = actions[actionIndex];
var playerStatistics = statistics[playerIndex];
switch (playerAction.Action)
{
case "bet":
playerStatistics.BetFirst = true;
playerStatistics.NumberOfActions++;
playerState.AmountPlaced += playerAction.Amount;
playerState.AmountAvailable -= playerAction.Amount;
highestAmountPlaced = playerState.AmountPlaced;
leadingPlayerIndex = playerIndex;
break;
case "raise":
playerStatistics.Raised = true;
playerStatistics.NumberOfActions++;
playerState.AmountPlaced += playerAction.Amount;
playerState.AmountAvailable -= playerAction.Amount;
highestAmountPlaced = playerState.AmountPlaced;
leadingPlayerIndex = playerIndex;
break;
case "pass":
playerState.Passed = true;
break;
}
actionIndex++;
}
if (actionIndex < actions.Count)
{
for (int playerIndex = 0; playerIndex < players.Count; playerIndex++)
{
var playerState = states[playerIndex];
if (playerState.Passed)
continue;
if (leadingPlayerIndex == playerIndex)
{
// This player is still leading the bet
break;
}
else if (playerState.Available > amountPlaced)
{
playerState.CouldReraise = true;
}
else
{
// They can't raise, but can they match, can they go all-in?
}
var playerAction = actions[actionIndex];
var playerStatistics = statistics[playerIndex];
switch (playerAction.Action)
{
case "raise":
playerStatistics.Reraised = true;
playerStatistics.NumberOfActions++;
playerState.AmountPlaced += playerAction.Amount;
playerState.AmountAvailable -= playerAction.Amount;
highestAmountPlaced = playerState.AmountPlaced;
leadingPlayerIndex = playerIndex;
break;
case "pass":
playerState.Passed = true;
break;
}
actionIndex++;
}
}
I'm building a racing game and I'm working on race times.
I try to build a system to start an instance of a timer with various options.
My little experience is putting me in crisis ... would some good soul want to help me?
This was the idea:
public class Timer {
public float counter;
public bool reset;
public string runtime = "--:--:--";
public string istant = "not istant";
public void startTimer()
{
/* inupdatealternative: counter += Time.deltaTime; */
if(reset == true)
{
counter = 0;
}
else
{
counter = Time.time;
}
var minutes = counter/60; // divide guitime by sixty (minutes)
var seconds = counter%60; // euclidean division (seconds)
var fraction = (counter * 100) % 100; // get fraction of seconds
runtime = string.Format ( "{0:00}:{1:00}:{2:000}", minutes, seconds, fraction);
Debug.Log("in Start: "+runtime);
}
public void resetTimer()
{
reset = true;
}
public string getTimerRuntime()
{
return runtime;
}
public string getTimerIstant()
{
istant = runtime;
return istant;
}
}
in update, for exemple:
var lapTimer = new Timer(); // create a new timer
if(Lap < Pilot.pilotlap )
{
lapTimer.startTimer();
Lap++
}
else if(Lap==Pilot.pilotlap)
{
timerLabel.text = lapTimer.getTimerIstant();
lapTimer.resetTimer();
lapTimer.startTimer();
}
in my head I'm sure someone has already dealt with it ... surely there will be something that manages the times and returns values in various ways: does it exist? or is there anyway how to make or build such a thing?
There is, it's called Stopwatch, it's THE class used in C# to use precise timers, and it's located in the System.Diagnostics namespace.
Using your Update() example, you can use it like this:
// Create a new stopwatch instance
// If the timer is used repeatedly, just instantiate one at start and re-use the same,
// to avoid garbage generation
Stopwatch lapTimer = new Stopwatch();
if(Lap < Pilot.pilotlap )
{
lapTimer.Start();
Lap++
}
else if(Lap==Pilot.pilotlap)
{
lapTimer.Stop();
// ElapsedMilliseconds returns exactly what it says, so you may need to format the value
// before passing it to the timerLabel.text
timerLabel.text = lapTimer.ElapsedMilliseconds.ToString();
lapTimer.Reset();
lapTimer.Start();
}
You can read about the class (its methods, fields and properties) here:
Stopwatch Class Documentation
You are doing a lot of unnecessary bool and local fields copiing and setting there. I would simply use something like
public class Timer
{
private float _startTime;
public bool IsRunning;
// you don't need an extra reset method
// simply pass it as a parameter
public void Start(bool reset = false)
{
if(IsRunning && !reset)
{
Debug.LogWarning("Timer is already running! If you wanted to restart consider passing true as parameter.");
return;
}
_startTime = Time.time;
Debug.Log("in Start: " + GetFormattedTime(_startTime));
IsRunning = true;
}
// depending what stop should do
// since this doesn't use any resources while running you could also simply
// only stick to the Start method and pass in true .. does basically the same
public void Stop()
{
IsRunning = false;
}
// I didn't see any difference between you two methods so I would simply use
public string GetCurrentTime()
{
if(!IsRunning)
{
Debug.LogWarning("Trying to get a time from a Timer that isn't running!");
return "--:--:---";
}
var timeDifference = Time.time - _startTime;
return GetFormattedTime(timeDifference);
}
private static string GetFormattedTime(float time)
{
// e.g. time = 74.6753
var minutes = Mathf.FloorToInt(time / 60f); // e.g. 1 (rounded down)
var seconds = Mathf.FloorToInt(time - 60f * minutes); // e.g. 14 (rounded down)
var fraction = Mathf.RoundToInt((time - seconds) * 1000f); // e.g. 676 (rounded down or up)
// Use a string interpolation for better readability
return $"{minutes:00}:{seconds:00}:{fraction:000}";
}
}
then in your Update you don't want to use
var lapTimer = new Timer(); // create a new timer
all the time since it would create a new timer and you wouldn't get any tracked time ... you rather would use it only once like
private Timer timer;
// just in case you want to keep track of needed times per lap
public List<string> lapTimes = new List<string>();
private void Awake()
{
timer = new Timer();
lapTimes.Clear();
}
private void Update()
{
...
if(Lap < Pilot.pilotlap)
{
timer.Start();
Lap++
}
else if(Lap == Pilot.pilotlap)
{
var currentTime = timer.GetCurrentTime();
timerLabel.text = currentTime;
lapTimes.Add(currentTime);
timer.Start(true)
}
...
}
Note that I don't know if this is all you have in Update or how you use it but you probably also do not want to (re)start the timer and count up the Lap every frame your conditions are true ... there should be more checks involved to make sure this can only be called once per lap ...
The problem I'm having is that I have an image that appears on two pages in the same book.
I wish my program to react (trigger a side-effect) only when encountering new images and ignore duplicates.Once it has done that I want it to reset so the process is repeated.
namespace testpad
{
class Interaction: Scene
{
public override string Name => "Interaction";
public static RectMap imageone = new RectMap()
{
X = 239,
Y = 199,
Width = 125,
Height = 18
};
public static RectMap imagetwo = new RectMap()
{
X = 217,
Y = 317,
Width = 428,
Height = 12
public override bool Match(ScriptBase script)
{
return script.Match(imageone, 90),
|| script.Match(imagetwo, 90);
}
public override void OnMatched(ScriptBase script)
{
if (script.Match(imageone, 90))
{
script.PressKey() { A = true });
Thread.Sleep(100);
}
else if (script.Match(imagetwo, 90))
{
script.Press() { B = true });
Thread.Sleep(1000);
}
}
}
If you expect a collection of objects and you want to take actions based upon their frequency of apparition the best solution would be a Dictionary<[Key],int> where the Key is the identifier (in your case a String with the name of the image) and the value represents the frequency of your key.Whenever you encounter an object (in your case lets call it image)with a given Key you increment the value stored at that key.Afterwards you can take decisions based upon resulting frequencies.
However in your case you just need to react only once , the first time you encounter a new image , so a HashSet<String> would be your best bet.
So your code would look like:
public void Iterate(IEnumerable<(string,object)>images)
{
HashSet<string>set=new HashSet<string>();
foreach(var item in images)
{
if(!set.TryGetValue(item.item1,out object obj))
{
set.Add(item.item1);
//React(item.item2) ---do something the first time you encounter the image
}
}
}
The following chart updates its values using a winforms Timer every second. The red line represents a constant target testing pressure, the blue line is the actual pressure read from a PLC object.
Y Axis = Testing Pressure, X Axis = Current Time, chart is updated with winforms timer at Interval = 1000 (every second)
The requirement is showing how many seconds have passed between the blue line reaching the constant required testing pressure (red line) and falling below the
constant required testing pressure.
The block that sets the constant required testing pressure:
...
chart1.ChartAreas[0].CursorY.Position = d;
chart1.ChartAreas[0].CursorY.LineWidth = 1;
chart1.ChartAreas[0].CursorY.LineColor = System.Drawing.Color.Red;
The part where I am stuck (this block is inside the method which updates the chart every second):
double plcTestpressure = ((uint)plc.Read("MD220")).ConvertToDouble();
double reqTestPressure = Convert.ToDouble(txtTestingPressure.Text);
if (plcTestpressure > reqTestPressure && !isAboveReq)
{
DateTime aboveReq = new DateTime();
aboveReq = DateTime.Now;
isAboveReq = true;
//this is for checking the plc read pressure
string currentpressure = ((uint)plc.Read("MD220")).ConvertToDouble().ToString();
}
//check if current pressure is below required and that pressure WAS above required a second ago...
if(plcTestpressure < reqTestPressure && isAboveReq)
{
DateTime belowReq = new DateTime();
belowReq = DateTime.Now;
tickCounter = (belowReq - aboveReq).TotalSeconds;
isAboveReq = false;
}
I have tried and stepped through this block, but it gives me a misleading answer in tickCounter (33 seconds when you can visually see on the graph 5 seconds have passed) and after the first time the tickCounter is assigned to, the aboveReq datetime stamp does not want to change.
Is there a better way to accomplish this goal? Am I going about it wrong? Should I provide more detail?
I would have to assume you have multiple variables named "aboveReq" since variables declared in an "if" block are local to the block. That means that when you access the "aboveReq" variable in the second "if" block, you aren't accessing the same variable.
Also does string currentpressure = ((uint)plc.Read("MD220")).ConvertToDouble().ToString(); really need to be within the if block (only tracking current pressure while above target)?
//Outside of method, top of class
private DateTime? _startTime = null;
private DateTime? _endTime = null;
//In method
string currentpressure = ((uint)plc.Read("MD220")).ConvertToDouble().ToString();
bool breachPressure = plcTestpressure > reqTestPressure;
if (breachPressure && _startTime == null)
{
_startTime = DateTime.Now;
}
else if(!breachPressure && _startTime != null)
{
_endTime = new DateTime();
var tickCounter = _endTime.Value.Subtract(_startTime.Value).TotalSeconds;
}
-----------------------------Edit---------------------------------------
Am I going about it wrong?
It would be considered cleaner if you moved the pressure monitoring logic to a separate class, thus keeping true to the single responsibility principle.
You can do that by implementing a pressure monitoring class that would raise events when the threshold is breached - something along the lines of -
public class PressureObserver
{
public event EventHandler<double> OnRaisedAboveThreshhold;
public event EventHandler<double> OnFellBelowThreshhold;
public double ThresholdPressure{ get; }
private double _lastMeasured = 0; //Initial Pressure
public PressureObserver(double thresholdPressure)
{
ThresholdPressure = thresholdPressure;
}
public void Observe(double plcTestpressure)
{
double pressureDelta = plcTestpressure - _lastMeasured;
if (pressureDelta > 0) //Pressure climbed
{
if(_lastMeasured < ThresholdPressure && //Last measurement was below threshold
plcTestpressure > ThresholdPressure) //This one is above, cross made
{
OnRaisedAboveThreshhold?.Invoke(this, plcTestpressure);
}
}
else if(pressureDelta < 0) //Pressure declined
{
if (_lastMeasured > ThresholdPressure && //Last measurement was above threshold
plcTestpressure < ThresholdPressure) //This one is below, cross made
{
OnFellBelowThreshhold?.Invoke(this, plcTestpressure);
}
}
_lastMeasured = plcTestpressure;
}
}
Then in your main class you would have fields
private PressureObserver _pressureObserver;
private DateTime _raisedAboveTime;
private DateTime _fellBelowTime;
private double _overpressureDuration;
you would define two methods to react to threshold changes
private void Obs_OnRaisedAboveTreshhold(object sender, double e)
{
//Code to do on raised above
_raisedAboveTime = DateTime.Now;
}
private void Obs_OnFellBelowTreshhold(object sender, double e)
{
//Code to do on fell below
_fellBelowTime = DateTime.Now;
_overpressureDuration = _fellBelowTime.Subtract(_raisedAboveTime).TotalSeconds;
}
and in the constructor you would subscribe to the observer class
_pressureObserver = new PressureObserver(60); //replace 60 with threshold
_pressureObserver.OnRaisedAboveThreshhold += Obs_OnRaisedAboveTreshhold;
_pressureObserver.OnFellBelowThreshhold += Obs_OnFellBelowTreshhold;
and in your tick timer you would just add
_pressureObserver.Observe(plcTestpressure);
I have a quiz game and at the end of the game I want to display a leaderboard of the top 10 people with their names and score for example: Ioue 500.
Right now I'm adding score every time user scores, and I'm checking the high score. However I'm not sure how to do the leaderboard. can someone guide me?
This script is where the user registers, it's a different scene.
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using System.Linq;
using System.Text.RegularExpressions;
using System.IO;
using System;
public class Registration : MonoBehaviour
{
public Text name;
public static Registration _instace;
private void Awake()
{
_instace = this;
}
/// <summary>
/// Performs data validation and registers user
/// </summary>
public void Register()
{
string data;
string currentUser = string.Empty;
string userName;
data = GetData(name);
if (data == null || string.IsNullOrEmpty(data))
{
Recolor(name, errorColor);
return;
}
else
{
userName = data;
currentUser += data + ";";
}
string previousDirectory = Directory.GetCurrentDirectory();
Directory.SetCurrentDirectory(Application.persistentDataPath);
if (File.Exists("Users.csv"))
{
string[] users = File.ReadAllLines("Users.csv");
foreach (string user in users)
{
string[] parts = user.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
}
}
File.AppendAllText("Users.csv", currentUser + Environment.NewLine);
Directory.SetCurrentDirectory(previousDirectory);
SceneManager.LoadScene(nextScene);
}
// Gets input data from control group provided
string GetData(Graphic group)
{
InputField inputField = group.GetComponentInChildren<InputField>();
if (inputField != null)
{
return inputField.text;
}
return null;
}
// Gets input data from control group provided
string GetDataD(Graphic group)
{
Dropdown inputField = group.GetComponentInChildren<Dropdown>();
if (inputField != null)
{
return inputField.captionText.text;
}
return null;
}
}
Where i add score is when player get's the right answer
if (isCorrect)
{
theAnswerIsCorrect = true;
playerScore += 20;
scoreDisplayText.text = "Score: " + playerScore.ToString();
}
This is where I compare the old and new score:
public void submitNewPlayerScore(int newScore)
{
if (newScore > playerProgress.highestScore)
{
playerProgress.highestScore = newScore;
SavePlayerProgress();
}
}
Now what I need is I will have many users playing, I just want to display the top 10 users with their names and high scores. Thank you!
Okay first thing : Why do you even need registration when it's offline?
You can omit the registration and just ask for a player name after quiz ends ( if you want to make offline highscores ).
Since you already have kind of highestScore you can just order it using Linq. I assume your code has access to all players ( because it's in csv file ) so I can recommend doing something like this :
List<Player> playerList = new List<Player>();
foreach(string user in File.ReadAllLines("Users.csv"))
{
playersList.Add(Player.Parse(user)); // If you have such method...
}
Now since you have all Players in one list you can use Linq to order it and take only x to display :
int x = 10; // how many records to take
IEnumerable<Player> highestX = playerList.OrderBy(player => player.highestScore).Take(x);
highestX is now holding 10 records with the highest score. You can just iterate through them and display details like highestX.ElementAt(0).Name or something like this.