[UI] Skin/Pet Selector

This commit is contained in:
riperiperi 2018-06-25 18:01:41 +01:00
parent 4ac38452dc
commit 00f8cd6727
8 changed files with 532 additions and 7 deletions

View file

@ -75,6 +75,7 @@
<Compile Include="UI\Panels\LiveSubpanels\UISubpanel.cs" />
<Compile Include="UI\Panels\LotControls\UIArchTouchHelper.cs" />
<Compile Include="UI\Panels\LotControls\UICallNeighborAlert.cs" />
<Compile Include="UI\Panels\LotControls\UISelectSkinAlert.cs" />
<Compile Include="UI\Panels\UIClockPanel.cs" />
<Compile Include="UI\Panels\UICutawayPanel.cs" />
<Compile Include="UI\Panels\UIHouseSelectPanel.cs" />
@ -111,6 +112,7 @@
<ProjectReference Include="..\..\..\FreeSO\Other\libs\FSOMonoGame\MonoGame.Framework\MonoGame.Framework.Windows.csproj">
<Project>{7de47032-a904-4c29-bd22-2d235e8d91ba}</Project>
<Name>MonoGame.Framework.Windows</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\FreeSO\TSOClient\FSO.UI\FSO.UI.csproj">
<Project>{73e2ad5b-720b-4ef3-9b7c-55931d0ec693}</Project>

View file

@ -8,7 +8,6 @@ using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Threading;
using LogThis;
using FSO.Common.Rendering.Framework;
using FSO.LotView;
using FSO.HIT;
@ -98,10 +97,11 @@ namespace Simitone.Client
FSO.LotView.WorldConfig.Current = new FSO.LotView.WorldConfig()
{
LightingMode = 3,
LightingMode = settings.LightingMode,
SmoothZoom = settings.SmoothZoom,
SurroundingLots = settings.SurroundingLotMode,
AA = settings.AntiAlias
AA = settings.AntiAlias,
Directional = settings.DirectionalLight3D
};
OperatingSystem os = Environment.OSVersion;
@ -123,7 +123,11 @@ namespace Simitone.Client
GameFacade.GraphicsDevice = GraphicsDevice;
GameFacade.GraphicsDeviceManager = Graphics;
GameFacade.Cursor = new CursorManager(GraphicsDevice);
if (!GameFacade.Linux) GameFacade.Cursor.Init(GlobalSettings.Default.TS1HybridPath, true);
if (!GameFacade.Linux)
{
CurLoader.BmpLoaderFunc = ImageLoader.BaseFunction;
GameFacade.Cursor.Init(GlobalSettings.Default.TS1HybridPath, true);
}
/** Init any computed values **/
GameFacade.Init();

View file

@ -0,0 +1,396 @@
using FSO.Client;
using FSO.Client.UI.Framework;
using FSO.Common;
using FSO.Common.Rendering.Framework;
using FSO.Common.Rendering.Framework.Camera;
using FSO.Common.Rendering.Framework.Model;
using FSO.Common.Utils;
using FSO.Content;
using FSO.Content.TS1;
using FSO.LotView.Model;
using FSO.SimAntics;
using FSO.SimAntics.Engine.TSOTransaction;
using FSO.SimAntics.NetPlay;
using FSO.SimAntics.NetPlay.Drivers;
using FSO.SimAntics.NetPlay.Model;
using FSO.Vitaboy;
using Microsoft.Xna.Framework;
using Simitone.Client.UI.Controls;
using Simitone.Client.UI.Panels.WorldUI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Simitone.Client.UI.Panels.LotControls
{
public class UISelectSkinAlert : UIMobileDialog
{
public UISelectSkinPanel NPanel;
/*
public short SelectedNeighbour
{
get
{
return NPanel.SelectedNeighbour;
}
}*/
public event Action<short> OnResult;
public UISelectSkinAlert(VMAvatar target, string type, VM vm)
{
var pet = type == "cat" || type == "dog";
Caption = pet ? "Adopt a Pet" : "Please Select Outfit";
SetHeight(600);
NPanel = new UISelectSkinPanel(target, type, vm);
NPanel.Position = new Microsoft.Xna.Framework.Vector2((Width - 1030) / 2, 110);
NPanel.OnResult += (res) => { OnResult?.Invoke(res); Close(); };
Add(NPanel);
}
public override void GameResized()
{
base.GameResized();
NPanel.Position = new Microsoft.Xna.Framework.Vector2((Width - 1030) / 2, 110);
}
}
public class UISelectSkinPanel : UIContainer{
public FSO.SimAntics.VM vm { get; set; }
public BasicCamera Camera;
public uint BaseGUID = 0x7FD96B54; //all of our templates should be of this kind.
public string CurrentCode;
public string CurrentSkin; //
public VMAvatar[] BodyAvatars;
public List<string> ActiveBodies;
public List<string> ActiveHandgroupTex;
public List<string> ActiveBodyTex;
public float HeadPosition = -9f;
public float BodyPosition = -9f;
public float HeadSpeed = 0f;
public float BodySpeed = 0f;
public float XLast = -1f;
public int HeadPositionLast = 0;
public int BodyPositionLast = 0;
public bool Pet;
public _3DTargetScene Scene;
public string CollectionType = "b";
public event Action<short> OnResult;
public UIBigButton OKButton;
public UIBigButton CancelButton;
public short ResultInd
{
get
{
var i = (int)DirectionUtils.PosMod(Math.Round(BodyPosition + 8), ActiveBodies.Count);
return (short)i;
}
}
public string ResultBody
{
get
{
var i = (int)DirectionUtils.PosMod(Math.Round(BodyPosition + 8), ActiveBodies.Count);
return ActiveBodies[i] + ",BODY=" + ActiveBodyTex[i];
}
}
public UISelectSkinPanel(VMAvatar target, string type, VM vm) {
this.vm = vm;
if (target != null)
{
BaseGUID = target.Object.OBJ.GUID;
CollectionType = type;
var bodyStrings = target.Object.Resource.Get<FSO.Files.Formats.IFF.Chunks.STR>(target.Object.OBJ.BodyStringID);
type = bodyStrings.GetString(1).Substring(4);
type = type.Substring(0, type.IndexOf('_'));
CurrentSkin = bodyStrings.GetString(14);
} else
{
Pet = true;
if (type == "cat") BaseGUID = 0x7BEA0977;
else if (type == "dog") BaseGUID = 0x4A70DF92;
}
Camera = new BasicCamera(GameFacade.GraphicsDevice, new Vector3(5, 1, 0), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
Camera.NearPlane = 0.001f;
Scene = new _3DTargetScene(GameFacade.GraphicsDevice, Camera, new Point(1030, 500), GlobalSettings.Default.AntiAlias?8:0);
Scene.Initialize(GameFacade.Scenes);
InitializeLot();
PopulateSimType(type);
OKButton = new UIBigButton(true);
OKButton.Caption = GameFacade.Strings.GetString("142", "0");
OKButton.Position = new Microsoft.Xna.Framework.Vector2((515 + 300)-137, 370);
OKButton.OnButtonClick += (btn) => { OnResult?.Invoke(ResultInd); };
OKButton.Width = 275;
Add(OKButton);
CancelButton = new UIBigButton(true);
CancelButton.Caption = GameFacade.Strings.GetString("142", "1");
CancelButton.Position = new Microsoft.Xna.Framework.Vector2((515 - 300) - 137, 370);
CancelButton.OnButtonClick += (btn) => { OnResult?.Invoke(-1); };
CancelButton.Width = 275;
Add(CancelButton);
}
public override void PreDraw(UISpriteBatch batch)
{
base.PreDraw(batch);
Camera.Position = new Vector3(10f, 4f, 0);
Camera.Target = new Vector3(4f, Pet?0f:0.8f, 0);
GameFacade.GraphicsDevice.RasterizerState = Microsoft.Xna.Framework.Graphics.RasterizerState.CullCounterClockwise;
Scene.Draw(GameFacade.GraphicsDevice);
GameFacade.GraphicsDevice.RasterizerState = Microsoft.Xna.Framework.Graphics.RasterizerState.CullNone;
}
public override void Draw(UISpriteBatch batch)
{
base.Draw(batch);
DrawLocalTexture(batch, Scene.Target, new Vector2(0, 0));
}
public override void Update(UpdateState state)
{
base.Update(state);
UpdateCarousel(state);
for (int i = 0; i < 18; i++)
{
var relPos = BodyPosition + i - 9;
relPos = (float)DirectionUtils.PosMod(relPos, 18);
if (relPos > 9) relPos += 6;
var angle = (relPos / 24) * (Math.PI * 2);
var pos = new Vector3(0 + 4.5f * (float)Math.Cos(angle), 0, 0 + 4.5f * (float)Math.Sin(angle));
BodyAvatars[i].Avatar.RotationY = -(float)angle + (float)Math.PI / 2;
var ang2 = (float)Math.Abs(DirectionUtils.PosMod(angle + Math.PI, Math.PI*2) - Math.PI);
//if (ang2 > Math.PI * 2) ang2 = 255;
var ava = BodyAvatars[i].Avatar;
ava.AmbientLight = Vector4.One * Math.Max(0, Math.Min(1, 2-ang2));
if (i != DirectionUtils.PosMod(-Math.Round(BodyPosition + 9), 18)) ava.AmbientLight *= 0.5f;
else ava.AmbientLight *= 1.1f;
ava.Position = pos;
ava.Scale = Vector3.One * (Pet ? 1 : 1 / 2f);
}
}
private void PopulateSimType(string simtype) // cat / dog / (ma/fa/mc/fc/uc) + (fit/fat/skn/chd)
{
CurrentCode = simtype;
var col = Content.Get().BCFGlobal.CollectionsByName[CollectionType];
var bodies = col.ClothesByAvatarType[simtype];
var tex = (TS1AvatarTextureProvider)Content.Get().AvatarTextures;
var texnames = tex.GetAllNames();
ActiveBodies = bodies;
ActiveBodyTex = bodies.Select(x => RemoveExt(texnames.FirstOrDefault(y => y.StartsWith(ExtractID(x, CurrentSkin))))).ToList();
ActiveHandgroupTex = bodies.Select(x => (RemoveExt(texnames.FirstOrDefault(y => y == "huao" + FindHG(x))) ?? "huao" + CurrentSkin).Substring(4)).ToList();
for (int i = 0; i < ActiveBodies.Count; i++)
{
if (ActiveBodyTex[i] == null)
{
ActiveBodyTex.RemoveAt(i);
ActiveHandgroupTex.RemoveAt(i);
ActiveBodies.RemoveAt(i--);
}
}
BodyPositionLast = 0;
PopulateReal();
}
private string FindHG(string item)
{
var ind = item.IndexOf('_');
if (ind != -1) item = item.Substring(ind);
return item;
}
private string RemoveExt(string item)
{
if (item == null) return null;
var ind = item.LastIndexOf('.');
if (ind != -1) return item.Substring(0, ind);
return item;
}
private string ExtractID(string item, string skncol)
{
var ind = item.IndexOf('_');
if (ind != -1) item = item.Substring(0, ind);
return item + skncol;
}
private string InsertSkinColor(string name, string skncol)
{
var ind = name.IndexOf('_');
if (ind != -1) name = name.Insert(ind, skncol);
return name;
}
private void UpdateCarousel(UpdateState state)
{
var frac = 60f / FSOEnvironment.RefreshRate;
var minSpeed = (Math.PI / 240f) * frac;
var mult = (float)Math.Pow(0.95, frac);
var moving = 0;
if (state.MouseState.LeftButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
{
var pos = GlobalPoint(state.MouseState.Position.ToVector2());
if (XLast == -1)
{
if (pos.Y > -100)
XLast = state.MouseState.X;
}
else
{
if (pos.Y > 0 && pos.Y < 375)
{
BodySpeed = ((XLast - state.MouseState.X) / 200f) * frac;
moving = 1;
}
XLast = state.MouseState.X;
}
}
else
{
XLast = -1;
}
BodySpeed = BodySpeed * mult;
if (Math.Abs(BodySpeed) < minSpeed && moving != 1)
{
var targInt = (int)Math.Round(BodyPosition);
BodySpeed = (float)Math.Max(-minSpeed, Math.Min(minSpeed, targInt - BodyPosition));
}
HeadSpeed = HeadSpeed * mult;
if (Math.Abs(HeadSpeed) < minSpeed && moving != 2)
{
var targInt = (int)Math.Round(HeadPosition);
HeadSpeed = (float)Math.Max(-minSpeed, Math.Min(minSpeed, targInt - HeadPosition));
}
HeadPosition += HeadSpeed;
BodyPosition += BodySpeed;
var room = 0;// vm.Context.GetRoomAt(LotTilePos.FromBigTile(28, 21, 1));
foreach (var body in BodyAvatars) body.SetRoom(65534);
var curbody = (int)BodyPosition;
if (curbody != BodyPositionLast)
{
FSO.HIT.HITVM.Get().PlaySoundEvent(FSO.Client.UI.Model.UISounds.Click);
int replacePos = (int)DirectionUtils.PosMod((-BodyPositionLast), 18);
int increment = 1;
if (curbody < BodyPositionLast) //start adding after last position's compliment position
{
increment = -1;
replacePos = (int)DirectionUtils.PosMod((-BodyPositionLast), 18);
}
var total = Math.Abs(curbody - BodyPositionLast);
for (int i = 0; i < total; i++)
{
if (increment == -1)
{
SetBody(BodyAvatars[replacePos], BodyPositionLast - 1);
}
else
{
SetBody(BodyAvatars[replacePos], BodyPositionLast + 17);
}
BodyPositionLast += increment;
replacePos = ((replacePos - increment) + 18) % 18;
}
BodyPositionLast = curbody;
}
}
private void SetBody(VMAvatar body, int i)
{
i = (int)DirectionUtils.PosMod(i, ActiveBodies.Count);
var oft = new Outfit() { TS1AppearanceID = ActiveBodies[i] + ".apr", TS1TextureID = ActiveBodyTex[i] };
if (!Pet)
{
var code = CurrentCode[0];
if (CurrentCode[1] != 'a') code = 'u';
var hg = ActiveHandgroupTex[i];
oft.LiteralHandgroup = new HandGroup()
{
TS1HandSet = true,
LightSkin = new HandSet()
{
LeftHand = new Hand()
{
Idle = new Gesture() { Name = "h" + code + "lo.apr", TexName = "huao" + hg },
Pointing = new Gesture() { Name = "h" + code + "lp.apr", TexName = "huap" + hg },
Fist = new Gesture() { Name = "h" + code + "lc.apr", TexName = "huac" + hg }
},
RightHand = new Hand()
{
Idle = new Gesture() { Name = "h" + code + "ro.apr", TexName = "huao" + hg },
Pointing = new Gesture() { Name = "h" + code + "rp.apr", TexName = "huap" + hg },
Fist = new Gesture() { Name = "h" + code + "rc.apr", TexName = "huac" + hg }
}
}
};
}
body.BodyOutfit = new FSO.SimAntics.Model.VMOutfitReference(oft);
}
private void PopulateReal()
{
int i = 0;
foreach (var body in BodyAvatars)
{
SetBody(body, 17 - i);
i++;
}
}
#region Lot Content Stuff
public void CleanupLastWorld()
{
if (vm == null) return;
foreach (var body in BodyAvatars)
{
body.Delete(true, vm.Context);
}
}
public void InitializeLot()
{
BodyAvatars = new VMAvatar[18];
for (int i = 0; i < 18; i++)
{
BodyAvatars[i] = (VMAvatar)vm.Context.CreateObjectInstance(BaseGUID, LotTilePos.OUT_OF_WORLD, Direction.NORTH, true).BaseObject;
BodyAvatars[i].SetValue(FSO.SimAntics.Model.VMStackObjectVariable.Hidden, 1);
Scene.Add(BodyAvatars[i].Avatar);
}
}
#endregion
}
}

View file

@ -261,6 +261,27 @@ namespace Simitone.Client.UI.Panels
BlockingDialog = null;
};
return;
case VMDialogType.TS1PetChoice:
case VMDialogType.TS1Clothes:
var ts1categories = new string[] { "b", "f", "s", "l", "w", "h" };
var pet = type == VMDialogType.TS1PetChoice;
var stackObj = info.Caller.Thread.Stack.Last().StackObject;
var skin = new UISelectSkinAlert(pet?null:(info.Caller as VMAvatar), pet?((stackObj as VMAvatar).IsCat?"cat":"dog"):ts1categories[info.Caller.Thread.TempRegisters[0]], vm);
BlockingDialog = skin;
UIScreen.GlobalShowDialog(skin, true);
skin.OnResult += (result) =>
{
vm.SendCommand(new VMNetDialogResponseCmd
{
ActorUID = info.Caller.PersistID,
ResponseCode = (byte)((result > -1)?1:0),
ResponseText = result.ToString()
});
BlockingDialog = null;
};
return;
}
var alert = new UIMobileAlert(options);
@ -432,7 +453,15 @@ namespace Simitone.Client.UI.Panels
{
obj = GotoObject;
}
if (obj != null)
if (obj is VMAvatar && state.CtrlDown)
{
//debug switch to avatar
vm.SendCommand(new VMNetChangeControlCmd()
{
TargetID = obj.ObjectID
});
}
else if (obj != null)
{
obj = obj.MultitileGroup.GetInteractionGroupLeader(obj);
if (obj is VMGameObject && ((VMGameObject)obj).Disabled > 0)

View file

@ -31,6 +31,7 @@ using Simitone.Client.UI.Panels;
using FSO.Client.UI.Controls;
using Simitone.Client.Utils;
using FSO.SimAntics.Utils;
using FSO.SimAntics.Model.TS1Platform;
namespace Simitone.Client.UI.Screens
{
@ -970,7 +971,7 @@ namespace Simitone.Client.UI.Screens
{
var marshal = new FSO.SimAntics.Marshals.VMMarshal();
marshal.Deserialize(file);
marshal.PlatformState = new VMTS1LotState();
vm.Load(marshal);
vm.Reset();
}

View file

@ -4,7 +4,10 @@ using FSO.LotView;
using Simitone.Client;
using Simitone.Windows.GameLocator;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
@ -61,6 +64,9 @@ namespace Simitone.Windows
#endregion
FSO.Files.ImageLoaderHelpers.BitmapFunction = BitmapReader;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
FSOEnvironment.SoftwareDepth = false;
FSOEnvironment.UseMRT = true;
@ -80,6 +86,7 @@ namespace Simitone.Windows
GlobalSettings.Default.TS1HybridEnable = true;
GlobalSettings.Default.TS1HybridPath = gameLocator.FindTheSims1();
GlobalSettings.Default.ClientVersion = "0";
GlobalSettings.Default.LightingMode = 3;
GlobalSettings.Default.AntiAlias = true;
GameFacade.DirectX = useDX;
@ -99,6 +106,91 @@ namespace Simitone.Windows
{
e.Cancel = !(GameFacade.Screens.CurrentUIScreen?.CloseAttempt() ?? true);
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject;
if (exception is OutOfMemoryException)
{
MessageBox.Show(e.ExceptionObject.ToString(), "Out of Memory! FreeSO needs to close.");
}
else
{
MessageBox.Show(e.ExceptionObject.ToString(), "A fatal error occured! Screenshot this dialog and post it on Discord.");
}
}
public static Tuple<byte[], int, int> BitmapReader(Stream str)
{
Bitmap image = (Bitmap)Bitmap.FromStream(str);
try
{
// Fix up the Image to match the expected format
image = (Bitmap)image.RGBToBGR();
var data = new byte[image.Width * image.Height * 4];
BitmapData bitmapData = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
if (bitmapData.Stride != image.Width * 4)
throw new NotImplementedException();
Marshal.Copy(bitmapData.Scan0, data, 0, data.Length);
image.UnlockBits(bitmapData);
return new Tuple<byte[], int, int>(data, image.Width, image.Height);
}
finally
{
image.Dispose();
}
}
// RGB to BGR convert Matrix
private static float[][] rgbtobgr = new float[][]
{
new float[] {0, 0, 1, 0, 0},
new float[] {0, 1, 0, 0, 0},
new float[] {1, 0, 0, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
};
internal static Image RGBToBGR(this Image bmp)
{
Image newBmp;
if ((bmp.PixelFormat & System.Drawing.Imaging.PixelFormat.Indexed) != 0)
{
newBmp = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
}
else
{
// Need to clone so the call to Clear() below doesn't clear the source before trying to draw it to the target.
newBmp = (Image)bmp.Clone();
}
try
{
System.Drawing.Imaging.ImageAttributes ia = new System.Drawing.Imaging.ImageAttributes();
System.Drawing.Imaging.ColorMatrix cm = new System.Drawing.Imaging.ColorMatrix(rgbtobgr);
ia.SetColorMatrix(cm);
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(newBmp))
{
g.Clear(Color.Transparent);
g.DrawImage(bmp, new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp.Width, bmp.Height, System.Drawing.GraphicsUnit.Pixel, ia);
}
}
finally
{
if (newBmp != bmp)
{
bmp.Dispose();
}
}
return newBmp;
}
}
#endif
}

View file

@ -47,6 +47,7 @@
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>

2
FreeSO

@ -1 +1 @@
Subproject commit b23f16c4de1792363bc25d94a4d34dc787d39899
Subproject commit 2ea906d86f4fea5c71bb62f3ec42b2dba3b701ac