I am working on a game in C#.
I have a list of events, that can happen. E.g
List<Event> _events = new List<Event>();
_events.Add(new Event("A goblin approaches", 2));
_events.Add(new Event("A gang of thieves approaches", 3));
_events.Add(new Event("Some money has been stolen", 3));
_events.Add(new Event("Three goblins approach", 4));
_events.Add(new Event("A guard dies of unknown causes",4));
The string in the Event constructor is the event name and the number is the difficulty.
I was wondering if I could also add code into the constructor that can be run later on if the event is called. I'd like different code in each event.
Something like
_events.Add(new Event("Some money has been stolen", 3, "Supply.Gold = Supply.Gold -50"));
_events[2].runcode
I hope this makes sense, thanks
With this approach you can create events with simple implementation. And also difficult events with a lot of code (by extending Event class).
public class EventContext
{
public int Gold { get; set; }
}
public interface IEvent
{
void Do(EventContext context);
}
public abstract class Event : IEvent
{
protected Event(string title, int difficulty)
{
Title = title;
Difficulty = difficulty;
}
public string Title { get; private set; }
public int Difficulty { get; private set; }
public abstract void Do(EventContext context);
}
public class SimpleEvent : Event
{
private readonly Action<EventContext> _callback;
public SimpleEvent(string title, int difficulty, Action<EventContext> callback)
: base(title, difficulty)
{
_callback = callback;
}
public override void Do(EventContext context)
{
_callback(context);
}
}
internal class Program
{
private static void Main(string[] args)
{
var gameContext = new EventContext {Gold = 100};
var events = new List<IEvent>();
events.Add(new SimpleEvent("Some money has been stolen", 3, context => context.Gold -= 10));
events.First().Do(gameContext);
}
}
But I believe it can be better to separate event type and event handler.
Related
Good day
I have code that worked on the old Pick pack and ship screen, the code would do a couple of changes on a QR code and then send it in to Acumatica.
With the new changes in Acumatica this is not possible any more.
What is the correct way to hook into the new (version 22) process barcode code?
Originally I could do this:
using WMSBase = PX.Objects.IN.WarehouseManagementSystemGraph<PX.Objects.IN.INScanReceive, PX.Objects.IN.INScanReceiveHost, PX.Objects.IN.INRegister, PX.Objects.IN.INScanReceive.Header>;
using PX.Objects;
using PX.Objects.IN;
namespace ExtScannerCode
{
public class INScanReceiveHostExtCustomPackage : PXGraphExtension<INScanReceive, INScanReceiveHost>
{
public static bool IsActive() => true;
#region Overrides ProcessItemBarcode
//ProcessItemBarcode
public delegate void ProcessItemBarcodeDelegate(string barcode);
[PXOverride]
public virtual void ProcessItemBarcode(string barcode, ProcessItemBarcodeDelegate baseMethod)
{
baseMethod?.Invoke(barcode);
}
#endregion
#region Overrides ProcessLotSerialBarcode
//ProcessLotSerialBarcode
public delegate void ProcessLotSerialBarcodeDelegate(string barcode);
[PXOverride]
public virtual void ProcessLotSerialBarcode(string barcode, ProcessLotSerialBarcodeDelegate baseMethod)
{
baseMethod?.Invoke(barcode);
}
#endregion
#region Overrides ProcessExpireDate
//ProcessLotSerialBarcode
public delegate void ProcessExpireDateDelegate(string barcode);
[PXOverride]
public virtual void ProcessExpireDate(string barcode, ProcessLotSerialBarcodeDelegate baseMethod)
{
baseMethod?.Invoke(barcode);
}
#endregion
}
[PXProtectedAccess]
public abstract class INScanReceiveHostExtProtectedAccess : PXGraphExtension<INScanReceiveHostExtCustomPackage, INScanReceive, INScanReceiveHost>
{
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ProcessItemBarcode(string barcode);
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ApplyState(string state);
[PXProtectedAccess(typeof(INScanReceive))]
protected abstract void ProcessLotSerialBarcode(string barcode);
}
}
With the new layout I am a bit lost, how would I hook into the new WarehouseManagementSystem? to process my barcodes
Referencing the private articles in the Acumatica Community site, you need to use an extension that has already been declared for each graph. For Pick Pack and Ship, the class definition would be
public class PickPackShipExt : PickPackShip.ScanExtension
{
}
From there, you would override the DecorateScanState function. There is an existing functionin the solution library, to show as an example. The code file is PX.Objects.SO\WMS\Modes\PickModes.cs.
You would inject into the state you are checking. Search for the graph you are overriding, so you can list states. For example, pick pack ship has these states:
protected override IEnumerable<ScanState<PickPackShip>> CreateStates()
{
yield return new ShipmentState();
yield return new LocationState();
yield return new InventoryItemState() { AlternateType = INPrimaryAlternateType.CPN, IsForIssue = true, SuppressModuleItemStatusCheck = true };
yield return new LotSerialState();
yield return new ExpireDateState() { IsForIssue = true };
yield return new ConfirmState();
yield return new CommandOrShipmentOnlyState();
}
So lets say we want to interject the lot serial number barcode reader. In this example, we want to add an X in front of what is scanned.
public class PickPackShipExt : PickPackShip.ScanExtension
{
[PXOverride]
public virtual ScanState<PickPackShip> DecorateScanState(ScanState<PickPackShip> original, Func<ScanState<PickPackShip>, ScanState<PickPackShip>> base_DecorateScanState)
{
var state = base_DecorateScanState(original);
//are you in pick mode?
if (state.ModeCode == PickMode.Value)
{
//are you scanning lot serial information?
if(state is LotSerialState lotSerialState)
{
//add some sort of validation/transoformation
lotSerialState.Intercept.GetByBarcode.ByOverride((basis, barcode, del) =>
{
//call the delegate, which just trims the barcode
string newBarcode = del(barcode);
//do something else with the barcode to transform. This example, add an X to the beginning and return
newBarcode = "X" + newBarcode;
return newBarcode;
});
}
}
return state;
}
}
You can search the solution for the state, and check the functions that are called. For example, the lot serial state code is:
public class LotSerialState : EntityState<string>
{
public const string Value = "LTSR";
public class value : BqlString.Constant<value> { public value() : base(LotSerialState.Value) { } }
public override string Code => Value;
protected override string StatePrompt => Msg.Prompt;
protected override bool IsStateActive() => Basis.ItemHasLotSerial;
protected override string GetByBarcode(string barcode) => barcode.Trim();
protected override Validation Validate(string lotSerial) => Basis.IsValid<WMSScanHeader.lotSerialNbr>(lotSerial, out string error) ? Validation.Ok : Validation.Fail(error);
protected override void Apply(string lotSerial) => Basis.LotSerialNbr = lotSerial;
protected override void ReportSuccess(string lotSerial) => Basis.Reporter.Info(Msg.Ready, lotSerial);
protected override void ClearState() => Basis.LotSerialNbr = null;
[PXLocalizable]
public abstract class Msg
{
public const string Prompt = "Scan the lot/serial number.";
public const string Ready = "The {0} lot/serial number is selected.";
public const string NotSet = "The lot/serial number is not selected.";
}
}
I hope this helps everyone get their customizations working.
I've recently added sounds into my LibGDX game. It's set up so that all the sounds I need are loaded into an AssetManager, and I retrieve them when I need to play them from a getSound(String name) method.
It works fine for a little bit when you play, but eventually it stops playing some, and sometimes most of them. I'd be fine with prioritizing the ones that are most recently played, and stopping the ones that are older if need be, but LibGDX doesn't seem to give you that much control over them.
The log error I get when this happens
E/AudioTrack: AudioFlinger could not create track, status: -12
E/SoundPool: Error creating AudioTrack.
It's usually playing quite a few at one time, probably around 10-20 small sounds at one time, depending on the situation, so I'm pretty sure that's the problem. I've read here about releasing a sound once it's played using a SoundPool, but I'm not entirely sure how to do that with LibGDX, or if it's possible, because I didn't see a class like that when I looked.
Also, I'm using ogg files for all of the sounds and none of them are very big.
Thanks!
Solved! By creating my own (kind of pseudo) Sound class, and a loader for it to be used with an AssetManager using assetManager.setLoader(). In the custom Sound class, I set every sound to keep track of their soundIds in an Array<Long>, then every time it plays I check the size of the array against a limit variable, stopping the oldest sound in the array. 0 meaning only able to be played one at a time, up to as far as I decide is necessary. This might not be a perfect solution, but it seems to work fairly well.
Criticism is welcome.
My created "Sound" class:
public class MySound {
private Sound sound;
private Array<Long> ids;
private int limit;
public MySound(Sound sound) {
this.sound = sound;
ids = new Array<Long>();
limit = 0;
}
public long play() {
limitSounds();
long id = sound.play();
ids.add(id);
return id;
}
public long play(float volume) {
limitSounds();
long id = sound.play(volume);
ids.add(id);
return id;
}
public long play(float volume, float pitch, float pan) {
limitSounds();
long id = sound.play(volume, pitch, pan);
ids.add(id);
return id;
}
public long loop() {
limitSounds();
long id = sound.loop();
ids.add(id);
return id;
}
public long loop(float volume) {
limitSounds();
long id = sound.loop(volume);
ids.add(id);
return id;
}
public long loop(float volume, float pitch, float pan) {
limitSounds();
long id = sound.loop(volume, pitch, pan);
ids.add(id);
return id;
}
public void stop() {
ids.clear();
sound.stop();
}
public void pause() {
sound.pause();
}
public void resume() {
sound.resume();
}
public void dispose() {
sound.dispose();
}
public void stop(long soundId) {
if (ids.contains(soundId, true)) {
ids.removeValue(soundId, true);
}
sound.stop(soundId);
}
public void pause(long soundId) {
sound.pause(soundId);
}
public void resume(long soundId) {
sound.resume(soundId);
}
public void setLooping(long soundId, boolean looping) {
sound.setLooping(soundId, looping);
}
public void setPitch(long soundId, float pitch) {
sound.setPitch(soundId, pitch);
}
public void setVolume(long soundId, float volume) {
sound.setVolume(soundId, volume);
}
public void setPan(long soundId, float pan, float volume) {
sound.setPan(soundId, pan, volume);
}
private void limitSounds () {
if (ids.size > limit) {
sound.stop(ids.get(0));
ids.removeIndex(0);
}
}
public void setLimit (int limit) {
this.limit = limit;
}
}
MySoundLoader class, for AssetManager:
public class MySoundLoader extends AsynchronousAssetLoader<MySound, MySoundLoader.SoundParameter> {
private MySound sound;
public MySoundLoader(FileHandleResolver resolver) {
super(resolver);
}
#Override
public Array<AssetDescriptor> getDependencies(String fileName, FileHandle file, SoundParameter parameter) {
return null;
}
#Override
public void loadAsync(AssetManager manager, String fileName, FileHandle file, SoundParameter parameter) {
sound = new MySound(Gdx.audio.newSound(file));
}
#Override
public MySound loadSync(AssetManager manager, String fileName, FileHandle file, SoundParameter parameter) {
MySound sound = this.sound;
this.sound = null;
return sound;
}
static public class SoundParameter extends AssetLoaderParameters<MySound> {
}
}
I am confused please can you help me? I read similar questions but its is not clear for me, thanks in advance for your patient and attention.
I want to return to onCreate data which retrieved from API call using Retrofit. Here is my function where i call Retrofit.
private void loadTimeZoneAPI(double latitude, double longitude, long timestamp, String apiKeyTz) {
String lat = Double.toString(latitude);
String lon = Double.toString(longitude);
String time = Long.toString(timestamp);
serviceTZ.getDataTZ(lat+","+lon, time, apiKeyTz).enqueue(new Callback<TimeZoneGoogle>() {
#Override
public void onResponse(Call<TimeZoneGoogle> call, Response<TimeZoneGoogle> response) {
TimeZoneGoogle result = response.body();
timeZone = result.getTimeZoneId();
}
#Override
public void onFailure(Call<TimeZoneGoogle> call, Throwable t) {
}
});
}
How i will return timeZone value to onCreate where i will use for calculation.
You need a callback listener for your data in MainActivity or any activity you have used for view.
for example:make interface like this
interface RetrofitListener{
onDataLoad(TimeZoneGoogle timeZoneGoogle);
}
then give a reference in Activity from which you are calling Retrofit class
public class ActivityTest extends AppCompatActivity implements ActivityTest.RetrofitListener {
RetrofitCall retrofitListener;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
retrofitListener = new RetrofitCall(this);
}
}
and here in RetrofitCall class
private class RetrofitCall {
private RetrofitListener retrofitListener;
public RetrofitCall(RetrofitListener retrofitListener) {
this.retrofitListener = retrofitListener;
}
void getData(){
getDataTZ(lat+","+lon, time, apiKeyTz).enqueue(new Callback<TimeZoneGoogle>() {
#Override
public void onResponse(Call<TimeZoneGoogle> call, Response<TimeZoneGoogle> response) {
TimeZoneGoogle result = response.body();
timeZone = result.getTimeZoneId();
}
#Override
public void onFailure(Call<TimeZoneGoogle> call, Throwable t) {
}
});
}
}
I am trying to learn IOC principle from this screencast
Inversion of Control from First Principles - Top Gear Style
I tried do as per screencast but i get an error while AutomaticFactory try create an object of AutoCue. AutoCue class has contructor which takes IClock and not SystemClock. But my question is , in screencast IClock is resolved with SystemClock while inside AutomaticFactory .But in my code , IClock does not get resolved . Am i missing something ?
class Program
{
static void Main(string[] args)
{
//var clarkson = new Clarkson(new AutoCue(new SystemClock()), new Megaphone());
//var clarkson = ClarksonFactory.SpawnOne();
var clarkson = (Clarkson)AutomaticFactory.GetOne(typeof(Clarkson));
clarkson.SaySomething();
Console.Read();
}
}
public class AutomaticFactory
{
public static object GetOne(Type type)
{
var constructor = type.GetConstructors().Single();
var parameters = constructor.GetParameters();
if (!parameters.Any()) return Activator.CreateInstance(type);
var args = new List<object>();
foreach(var parameter in parameters)
{
var arg = GetOne(parameter.ParameterType);
args.Add(arg);
}
var result = Activator.CreateInstance(type, args.ToArray());
return result;
}
}
public class Clarkson
{
private readonly AutoCue _autocue;
private readonly Megaphone _megaphone;
public Clarkson(AutoCue autocue,Megaphone megaphone)
{
_autocue = autocue;
_megaphone =megaphone;
}
public void SaySomething()
{
var message = _autocue.GetCue();
_megaphone.Shout(message);
}
}
public class Megaphone
{
public void Shout(string message)
{
Console.WriteLine(message);
}
}
public interface IClock
{
DateTime Now { get; }
}
public class SystemClock : IClock
{
public DateTime Now { get { return DateTime.Now; } }
}
public class AutoCue
{
private readonly IClock _clock;
public AutoCue(IClock clock)
{
_clock = clock;
}
public string GetCue()
{
DateTime now = _clock.Now;
if (now.DayOfWeek == DayOfWeek.Sunday)
{
return "Its a sunday!";
}
else
{
return "I have to work!";
}
}
}
What you basically implemented is a small IoC container that is able to auto-wire object graphs. But your implementation is only able to create object graphs of concrete objects. This makes your code violate the Dependency Inversion Principle.
What's missing from the implementation is some sort of Register method that tells your AutomaticFactory that when confronted with an abstraction, it should resolve the registered implementation. That could look as follows:
private static readonly Dictionary<Type, Type> registrations =
new Dictionary<Type, Type>();
public static void Register<TService, TImplementation>()
where TImplementation : class, TService
where TService : class
{
registrations.Add(typeof(TService), typeof(TImplementation));
}
No you will have to do an adjustment to the GetOne method as well. You can add the following code at the start of the GetOne method:
if (registrations.ContainsKey(type))
{
type = registrations[type];
}
That will ensure that if the supplied type is registered in the AutomaticFactory as TService, the mapped TImplementation will be used and the factory will continue using this implementation as the type to build up.
This does mean however that you now have to explicitly register the mapping between IClock and SystemClock (which is a quite natural thing to do if you're working with an IoC container). You must make this mapping before the first instance is resolved from the AutomaticFactory. So you should add the following line to to the beginning of the Main method:
AutomaticFactory.Register<IClock, SystemClock>();
I have small WPF application. There are 5 projects in solution.
I want separate DOMAIN classes with UI ENTITIES and I want to use AUTOMAPPER.
You can download whole solution here: TestWPFAutomapper.zip
Domain class(Domain.Source.cs) with UI Entity(Entities.Destination.cs) have same signature.
In Entities.Destination.cs I would like to put other logic.
namespace DOMAIN
{
public class Source
{
public int Id { get; set; }
public int Position { get; set; }
}
}
using System.ComponentModel;
namespace ENITITIES
{
public class Destination : INotifyPropertyChanged
{
private int _id;
private int _position;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public int Position
{
get { return _position; }
set
{
_position = value;
OnPropertyChanged("Position");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
My data comes from DAL.DataContext using Entity Framework with CodeFirst. Here I´m using Source class.
using System.Data.Entity;
using DOMAIN;
namespace DAL
{
public class DataContext : DbContext
{
public DbSet<Source> Sources { get; set; }
}
}
Mapping is in BL.MyAppLogic.cs . In this class I have property Items which is ObservableCollection.
After puting another item into DB for Source class collection get refresh but for Destination is not refreshing.
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using AutoMapper;
using DAL;
using DOMAIN;
using ENITITIES;
namespace BL
{
public class MyAppLogic
{
private readonly DataContext _dataContext = new DataContext();
public ObservableCollection<Source> Items { get; set; }
//public ObservableCollection<Destination> Items { get; set; }
public MyAppLogic()
{
Database.SetInitializer(new MyInitializer());
Mapping();
_dataContext.Sources.Load();
Items = _dataContext.Sources.Local;
//Items = Mapper.Map<ObservableCollection<Source>, ObservableCollection<Destination>>(_dataContext.Sources.Local);
}
private void Mapping()
{
Mapper.CreateMap<Source, Destination>().ReverseMap();
// I tried also Mapper.CreateMap<ObservableCollection<Source>, ObservableCollection<Destination>>().ReverseMap();
}
public int GetLastItem()
{
return _dataContext.Database.SqlQuery<int>("select Position from Sources").ToList().LastOrDefault();
}
public void AddNewItem(Destination newItem)
{
_dataContext.Sources.Add(Mapper.Map<Destination, Source>(newItem));
_dataContext.SaveChanges();
}
}
}
My problem is not with mapping, that’s works good, but with refreshing collection after adding or removing items from db. If I use DOMAIN.Source class everything works, collection is refreshing. But when I’m using ENTITIES.Destination data comes from DB and also I can put som new data to DB but refresing ObservableCollection is not working.
Please try to comment lines(14 & 23) in BL.MyAppLogic.cs and uncomment(15 & 24) and you’ll see what I mean.
Thank you for any help.
I got it but I don´t know if is correct.
Local has CollectionChanged event
so in constructor I put these lines
public MyAppLogic()
{
Database.SetInitializer(new MyInitializer());
Mapping();
_dataContext.Sources.Load();
_dataContext.Sources.Local.CollectionChanged += SourcesCollectionChanged;
Items = Mapper.Map<ObservableCollection<Source>, ObservableCollection<Destination>>(_dataContext.Sources.Local);
}
and handler looks
private void SourcesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var source = sender as ObservableCollection<Source>;
Mapper.Map(source, Items);
}
Now is my collection automating refreshing when I put something to DB in my UI.
Looks like automapper don´t put reference into Items, but create new instance.