diff --git a/Client/Simitone/Simitone.Client/Content/cas.fsov b/Client/Simitone/Simitone.Client/Content/cas.fsov new file mode 100644 index 0000000..3df89f3 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/cas.fsov differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_adult.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_adult.png new file mode 100644 index 0000000..9e5ee5c Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_adult.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_bio.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_bio.png new file mode 100644 index 0000000..b73efbe Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_bio.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_bio_bg.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_bio_bg.png new file mode 100644 index 0000000..7eaf5fe Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_bio_bg.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_cat_del.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_cat_del.png new file mode 100644 index 0000000..3101115 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_cat_del.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_cat_edit.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_cat_edit.png new file mode 100644 index 0000000..af34fc9 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_cat_edit.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_child.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_child.png new file mode 100644 index 0000000..192d9a9 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_child.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_female.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_female.png new file mode 100644 index 0000000..3f011bd Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_female.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_male.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_male.png new file mode 100644 index 0000000..068b263 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_male.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_new_plus.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_new_plus.png new file mode 100644 index 0000000..aa5b0d2 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_new_plus.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_per.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_per.png new file mode 100644 index 0000000..b8a3ba0 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_per.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_rand.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_rand.png new file mode 100644 index 0000000..1ac507f Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_rand.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_sim.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_sim.png new file mode 100644 index 0000000..b8ac04a Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_sim.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skindrk.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skindrk.png new file mode 100644 index 0000000..6386bc2 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skindrk.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skinlgt.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skinlgt.png new file mode 100644 index 0000000..4faff3a Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skinlgt.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skinmed.png b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skinmed.png new file mode 100644 index 0000000..d1d8699 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/cas/cas_skinmed.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/common/btn_accept.png b/Client/Simitone/Simitone.Client/Content/uigraphics/common/btn_accept.png new file mode 100644 index 0000000..ccb4f5b Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/common/btn_accept.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/common/btn_back.png b/Client/Simitone/Simitone.Client/Content/uigraphics/common/btn_back.png new file mode 100644 index 0000000..1c3b79d Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/common/btn_back.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_arch.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_arch.png new file mode 100644 index 0000000..8d889ce Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_arch.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_door.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_door.png new file mode 100644 index 0000000..7c897a3 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_door.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_fire.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_fire.png new file mode 100644 index 0000000..581beba Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_fire.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_flor.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_flor.png new file mode 100644 index 0000000..0aebb38 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_flor.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_objs.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_objs.png new file mode 100644 index 0000000..887ca27 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_objs.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_outs.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_outs.png new file mode 100644 index 0000000..3bfdb9e Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_outs.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_roof.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_roof.png new file mode 100644 index 0000000..daabbc2 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_roof.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_stai.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_stai.png new file mode 100644 index 0000000..e287046 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_stai.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_terr.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_terr.png new file mode 100644 index 0000000..acb04af Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_terr.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_tree.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_tree.png new file mode 100644 index 0000000..426a96d Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_tree.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_wall.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_wall.png new file mode 100644 index 0000000..33d9b17 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_wall.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_walp.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_walp.png new file mode 100644 index 0000000..4de7650 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_walp.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_watr.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_watr.png new file mode 100644 index 0000000..f5979d6 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_watr.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_wind.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_wind.png new file mode 100644 index 0000000..7997eac Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_build_wind.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_cancel.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_cancel.png new file mode 100644 index 0000000..67e5946 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat/cat_cancel.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat_thumb_bg.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat_thumb_bg.png new file mode 100644 index 0000000..e297d28 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/cat_thumb_bg.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_neigh.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_neigh.png new file mode 100644 index 0000000..8ee76dd Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_neigh.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_quit.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_quit.png new file mode 100644 index 0000000..e883657 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_quit.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_save.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_save.png new file mode 100644 index 0000000..23f8e17 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/modes/opt_save.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/query_title.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/query_title.png new file mode 100644 index 0000000..a12b639 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/query_title.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_buy.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_buy.png new file mode 100644 index 0000000..eb21439 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_buy.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_cross.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_cross.png new file mode 100644 index 0000000..36b00f3 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_cross.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_place.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_place.png new file mode 100644 index 0000000..adb7392 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_place.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_rotccw.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_rotccw.png new file mode 100644 index 0000000..f32c615 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_rotccw.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_rotcw.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_rotcw.png new file mode 100644 index 0000000..801b9e7 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_rotcw.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_tool.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_tool.png new file mode 100644 index 0000000..25d9451 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_tool.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_toolc.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_toolc.png new file mode 100644 index 0000000..2cb7897 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_toolc.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_tools.png b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_tools.png new file mode 100644 index 0000000..db51bc5 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/live/touch/touch_tools.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_back.png b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_back.png new file mode 100644 index 0000000..d214c0b Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_back.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_cas.png b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_cas.png new file mode 100644 index 0000000..75195b0 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_cas.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_downt.png b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_downt.png new file mode 100644 index 0000000..f0b2830 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_downt.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_magic.png b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_magic.png new file mode 100644 index 0000000..cf2f2cb Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_magic.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_opt.png b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_opt.png new file mode 100644 index 0000000..a6454b5 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_opt.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_studio.png b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_studio.png new file mode 100644 index 0000000..e9cf8f7 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_studio.png differ diff --git a/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_vacat.png b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_vacat.png new file mode 100644 index 0000000..4510bc5 Binary files /dev/null and b/Client/Simitone/Simitone.Client/Content/uigraphics/ngbh/ngbh_vacat.png differ diff --git a/Client/Simitone/Simitone.Client/UI/Panels/CAS/UIFamilyCASPanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/CAS/UIFamilyCASPanel.cs new file mode 100644 index 0000000..6547ef9 --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/CAS/UIFamilyCASPanel.cs @@ -0,0 +1,206 @@ +using FSO.Client; +using FSO.Client.UI.Controls; +using FSO.Client.UI.Framework; +using FSO.Common.Utils; +using FSO.Content; +using FSO.SimAntics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Simitone.Client.UI.Controls; +using Simitone.Client.UI.Model; +using Simitone.Client.UI.Screens; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Simitone.Client.UI.Panels.CAS +{ + public class UIFamilyCASPanel : UIContainer + { + public UIDiagonalStripe NameStripe; + public UIDiagonalStripe ListStripe; + + public UITextBox SecondName; + public UIAvatarListPanel AvatarList; + public UICategorySwitcher AvatarOptions; + + public Action ModifySim; + + private float _ShowI; + public float ShowI + { + get + { + return _ShowI; + } + set + { + if (value < 1 && AvatarOptions.CategoryExpand == 1) AvatarOptions.Close(); + ListStripe.Visible = value > 0; + NameStripe.Visible = value > 0; + NameStripe.X = (1 - value) * UIScreen.Current.ScreenWidth; + NameStripe.Y = 30; + NameStripe.BodySize = new Point((int)(value * UIScreen.Current.ScreenWidth), NameStripe.BodySize.Y); + ListStripe.BodySize = new Point((int)(value * UIScreen.Current.ScreenWidth), ListStripe.BodySize.Y); + AvatarList.X = (1-value) * (-UIScreen.Current.ScreenWidth); + SecondName.X = (1 - value) * (UIScreen.Current.ScreenWidth); + _ShowI = value; + } + } + + private List AvatarCategories = new List() + { + new UICategory() { ID = 0, IconName = "live_motives.png" }, //dummy + new UICategory() { ID = 1, IconName = "cas_cat_edit.png" }, + new UICategory() { ID = 2, IconName = "cas_cat_del.png" }, + }; + + private int ActiveSelection = -1; + private List Avatars; + + public UIFamilyCASPanel(List avatar) + { + Avatars = avatar; + + AvatarOptions = new UICategorySwitcher(); + AvatarOptions.InitCategories(AvatarCategories); + AvatarOptions.MainButton.Visible = false; + AvatarOptions.OnCategorySelect += AvatarOptions_OnCategorySelect; + Add(AvatarOptions); + + NameStripe = new UIDiagonalStripe(new Point(0, 75), UIDiagonalStripeSide.LEFT, UIStyle.Current.Bg); + NameStripe.Position = new Vector2(UIScreen.Current.ScreenWidth, 30); + Add(NameStripe); + + SecondName = new UITextBox(); + SecondName.TextMargin = new Rectangle(); + SecondName.SetSize(UIScreen.Current.ScreenWidth, 60); + SecondName.Alignment = TextAlignment.Center; + SecondName.TextStyle = SecondName.TextStyle.Clone(); + SecondName.TextStyle.Size = 37; + SecondName.TextStyle.Color = UIStyle.Current.SecondaryText; + SecondName.Position = new Vector2(0, 40); + Add(SecondName); + + ListStripe = new UIDiagonalStripe(new Point(0, 125), UIDiagonalStripeSide.RIGHT, UIStyle.Current.Bg); + ListStripe.Position = new Vector2(0, UIScreen.Current.ScreenHeight - 145); + Add(ListStripe); + + AvatarList = new UIAvatarListPanel(avatar); + AvatarList.Y = UIScreen.Current.ScreenHeight - 145; + AvatarList.OnSelection += AvatarList_OnSelection; + Add(AvatarList); + + Reset(); + ShowI = ShowI; + } + + private void AvatarOptions_OnCategorySelect(int obj) + { + if (obj == 0) return; + ModifySim?.Invoke((obj == 2), ActiveSelection); + AvatarOptions.Select(0); + } + + public void Reset() + { + AvatarList.InitAvatarList(); + ActiveSelection = -1; + } + + private void AvatarList_OnSelection(int obj) + { + if (obj >= Avatars.Count) + { + ModifySim?.Invoke(false, -1); + } else + { + AvatarOptions.X = AvatarOptions.X = UIScreen.Current.ScreenWidth / 2 - (Avatars.Count()) * 50 + obj * 100 - 44; + AvatarOptions.Y = UIScreen.Current.ScreenHeight - 145; + if (AvatarOptions.CategoryExpand < 1 || ActiveSelection == -1 || ActiveSelection == obj) + AvatarOptions.Open(); + } + ActiveSelection = obj; + } + + public override void GameResized() + { + base.GameResized(); + AvatarList.Y = UIScreen.Current.ScreenHeight - 145; + ListStripe.Position = new Vector2(0, UIScreen.Current.ScreenHeight - 145); + SecondName.SetSize(UIScreen.Current.ScreenWidth, 60); + + if (ActiveSelection > -1) + { + AvatarOptions.X = UIScreen.Current.ScreenWidth / 2 - (Avatars.Count()) * 50 + ActiveSelection*100 - 44; + AvatarOptions.Y = UIScreen.Current.ScreenHeight - 145; + } + + AvatarList.InitAvatarList(); + } + } + + public class UIAvatarListPanel : UIContainer + { + private List Avatars; + private List Btns = new List(); + private Texture2D Bg; + public event Action OnSelection; + public UIAvatarListPanel(List avatar) + { + Avatars = avatar; + + InitAvatarList(); + } + + public void InitAvatarList() + { + int i = 0; + foreach (var btn in Btns) + { + Remove(btn); + } + Btns.Clear(); + + i = 0; + var baseX = UIScreen.Current.ScreenWidth/2 - (Avatars.Count()) * 50; + foreach (var fam in Avatars) + { + var btn = new UIAvatarSelectButton(UIIconCache.GetObject(fam)); + btn.Opacity = 1f; + var id = i; + btn.OnButtonClick += (b) => { Select(id); }; + btn.Name = fam.Name; + btn.X = baseX + (i++) * 100; + btn.Y = 50; + Btns.Add(btn); + Add(btn); + } + var btn2 = new UIAvatarSelectButton(Content.Get().CustomUI.Get("cas_new_plus.png").Get(GameFacade.GraphicsDevice)); + btn2.Opacity = 1f; + var id2 = i; + btn2.OnButtonClick += (b) => { Select(id2); }; + btn2.X = baseX + (i++) * 100; + btn2.Y = 50; + Btns.Add(btn2); + Add(btn2); + } + + private void Select(int selected) + { + int i = 0; + foreach (var btn in Btns) + { + btn.Outlined = (i++) == selected; + } + OnSelection?.Invoke(selected); + } + + public override void Draw(UISpriteBatch batch) + { + base.Draw(batch); + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/CAS/UISimCASPanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/CAS/UISimCASPanel.cs new file mode 100644 index 0000000..fbd9273 --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/CAS/UISimCASPanel.cs @@ -0,0 +1,394 @@ +using FSO.Client; +using FSO.Client.UI.Controls; +using FSO.Client.UI.Framework; +using FSO.Client.UI.Model; +using FSO.Common.Rendering.Framework.IO; +using FSO.Common.Rendering.Framework.Model; +using FSO.Common.Utils; +using FSO.Content; +using FSO.HIT; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Simitone.Client.UI.Controls; +using Simitone.Client.UI.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Simitone.Client.UI.Panels.CAS +{ + public class UISimCASPanel : UIContainer + { + public UILabel FirstNameTitle; + public UITextBox FirstNameTextBox; + public UIStencilButton SimTabButton; + public UIStencilButton PersonalityTabButton; + public UIStencilButton BioTabButton; + + public UILabel SimGenderTitle; + public UILabel SimAgeTitle; + public UILabel SimSkinTitle; + public UIStencilButton SimMaleBtn; + public UIStencilButton SimFemaleBtn; + public UIStencilButton SimAdultBtn; + public UIStencilButton SimChildBtn; + public UIStencilButton SimSkinLgtBtn; + public UIStencilButton SimSkinMedBtn; + public UIStencilButton SimSkinDrkBtn; + public UIStencilButton SimRandomBtn; + + public UILabel PerNeatTitle; + public UILabel PerOutgoingTitle; + public UILabel PerActiveTitle; + public UILabel PerPlayfulTitle; + public UILabel PerNiceTitle; + private Texture2D PersonalityTex; + public UICASPersonalityBar[] Personalities = new UICASPersonalityBar[5]; + + public UILabel BioTitle; + public UIImage BioBG; + public UITextEdit BioEdit; + + public string AType = "ma"; + public string SkinType = "lgt"; + + public event Action OnCollectionChange; + public event Action OnRandom; + + public int ActiveTab; + + public UISimCASPanel() + { + var ui = Content.Get().CustomUI; + var gd = GameFacade.GraphicsDevice; + + SimTabButton = new UIStencilButton(ui.Get("cas_sim.png").Get(gd)); + SimTabButton.Position = new Vector2(); + SimTabButton.OnButtonClick += (btn) => { SetTab(0); }; + Add(SimTabButton); + + PersonalityTabButton = new UIStencilButton(ui.Get("cas_per.png").Get(gd)); + PersonalityTabButton.Position = new Vector2(0, 93); + PersonalityTabButton.OnButtonClick += (btn) => { SetTab(1); }; + Add(PersonalityTabButton); + + BioTabButton = new UIStencilButton(ui.Get("cas_bio.png").Get(gd)); + BioTabButton.Position = new Vector2(0, 186); + BioTabButton.OnButtonClick += (btn) => { SetTab(2); }; + Add(BioTabButton); + + FirstNameTitle = new UILabel(); + FirstNameTitle.Caption = "First Name"; + FirstNameTitle.Position = new Vector2(96, 6); + InitLabel(FirstNameTitle, 15); + FirstNameTitle.CaptionStyle.Color = UIStyle.Current.SecondaryText; + + FirstNameTextBox = new UITextBox(); + FirstNameTextBox.Position = new Vector2(94, 26); + FirstNameTextBox.Alignment = TextAlignment.Center; + FirstNameTextBox.SetSize(401, 48); + FirstNameTextBox.TextStyle = FirstNameTextBox.TextStyle.Clone(); + FirstNameTextBox.TextStyle.Color = UIStyle.Current.Text; + FirstNameTextBox.TextStyle.Size = 25; + Add(FirstNameTextBox); + + // SIM TAB + + SimGenderTitle = new UILabel(); + SimGenderTitle.Caption = "Gender"; + SimGenderTitle.Size = Vector2.One; + SimGenderTitle.Position = new Vector2(113 + 32, 95); + SimGenderTitle.Alignment = TextAlignment.Middle | TextAlignment.Center; + InitLabel(SimGenderTitle, 15); + + SimAgeTitle = new UILabel(); + SimAgeTitle.Caption = "Age"; + SimAgeTitle.Size = Vector2.One; + SimAgeTitle.Position = new Vector2(212 + 32, 95); + SimAgeTitle.Alignment = TextAlignment.Middle | TextAlignment.Center; + InitLabel(SimAgeTitle, 15); + + SimSkinTitle = new UILabel(); + SimSkinTitle.Caption = "Skin Color"; + SimSkinTitle.Size = Vector2.One; + SimSkinTitle.Position = new Vector2(302 + 45, 95); + SimSkinTitle.Alignment = TextAlignment.Middle | TextAlignment.Center; + InitLabel(SimSkinTitle, 15); + + SimMaleBtn = new UIStencilButton(ui.Get("cas_male.png").Get(gd)); + SimMaleBtn.OnButtonClick += (btn) => ChangeType(0, 'm'); + SimMaleBtn.Position = new Vector2(113, 114); + Add(SimMaleBtn); + SimFemaleBtn = new UIStencilButton(ui.Get("cas_female.png").Get(gd)); + SimFemaleBtn.OnButtonClick += (btn) => ChangeType(0, 'f'); + SimFemaleBtn.Position = new Vector2(113, 191); + Add(SimFemaleBtn); + + SimAdultBtn = new UIStencilButton(ui.Get("cas_adult.png").Get(gd)); + SimAdultBtn.OnButtonClick += (btn) => ChangeType(1, 'a'); + SimAdultBtn.Position = new Vector2(212, 114); + Add(SimAdultBtn); + SimChildBtn = new UIStencilButton(ui.Get("cas_child.png").Get(gd)); + SimChildBtn.OnButtonClick += (btn) => ChangeType(1, 'c'); + SimChildBtn.Position = new Vector2(212, 191); + Add(SimChildBtn); + + SimSkinLgtBtn = new UIStencilButton(ui.Get("cas_skinlgt.png").Get(gd)); + SimSkinLgtBtn.OnButtonClick += (btn) => ChangeSkin("lgt"); + SimSkinLgtBtn.Position = new Vector2(302, 114); + Add(SimSkinLgtBtn); + SimSkinMedBtn = new UIStencilButton(ui.Get("cas_skinmed.png").Get(gd)); + SimSkinMedBtn.OnButtonClick += (btn) => ChangeSkin("med"); + SimSkinMedBtn.Position = new Vector2(302, 164); + Add(SimSkinMedBtn); + SimSkinDrkBtn = new UIStencilButton(ui.Get("cas_skindrk.png").Get(gd)); + SimSkinDrkBtn.OnButtonClick += (btn) => ChangeSkin("drk"); + SimSkinDrkBtn.Position = new Vector2(302, 214); + Add(SimSkinDrkBtn); + + SimRandomBtn = new UIStencilButton(ui.Get("cas_rand.png").Get(gd)); + SimRandomBtn.Position = new Vector2(405, 142); + SimRandomBtn.OnButtonClick += SimRandomBtn_OnButtonClick; + Add(SimRandomBtn); + + //PERSONALITY TAB + + PerNeatTitle = new UILabel(); + PerNeatTitle.Position = new Vector2(99, 85); + PerNeatTitle.Size = new Vector2(110, 1); + PerNeatTitle.Alignment = TextAlignment.Right; + PerNeatTitle.Caption = "Neat:"; + InitLabel(PerNeatTitle, 15); + + PerOutgoingTitle = new UILabel(); + PerOutgoingTitle.Position = new Vector2(99, 122); + PerOutgoingTitle.Size = new Vector2(110, 1); + PerOutgoingTitle.Alignment = TextAlignment.Right; + PerOutgoingTitle.Caption = "Outgoing:"; + InitLabel(PerOutgoingTitle, 15); + + PerActiveTitle = new UILabel(); + PerActiveTitle.Position = new Vector2(99, 160); + PerActiveTitle.Size = new Vector2(110, 1); + PerActiveTitle.Alignment = TextAlignment.Right; + PerActiveTitle.Caption = "Active:"; + InitLabel(PerActiveTitle, 15); + + PerPlayfulTitle = new UILabel(); + PerPlayfulTitle.Position = new Vector2(99, 200); + PerPlayfulTitle.Size = new Vector2(110, 1); + PerPlayfulTitle.Alignment = TextAlignment.Right; + PerPlayfulTitle.Caption = "Playful:"; + InitLabel(PerPlayfulTitle, 15); + + PerNiceTitle = new UILabel(); + PerNiceTitle.Position = new Vector2(99, 238); + PerNiceTitle.Size = new Vector2(110, 1); + PerNiceTitle.Alignment = TextAlignment.Right; + PerNiceTitle.Caption = "Nice:"; + InitLabel(PerNiceTitle, 15); + + PersonalityTex = ui.Get("skill.png").Get(gd); + + for (int i=0; i<5; i++) + { + Personalities[i] = new UICASPersonalityBar(this); + Personalities[i].Position = new Vector2(215, 87 + i*38); + Add(Personalities[i]); + } + + //BIO TAB + BioTitle = new UILabel(); + BioTitle.Position = new Vector2(99, 85); + BioTitle.Caption = "Bio:"; + InitLabel(BioTitle, 15); + + BioBG = new UIImage(ui.Get("cas_bio_bg.png").Get(gd)).With9Slice(15, 15, 15, 15); + BioBG.Position = new Vector2(99, 112); + BioBG.SetSize(390, 152); + Add(BioBG); + + BioEdit = new UITextEdit(); + BioEdit.Position = new Vector2(107, 122); + BioEdit.SetSize(371, 133); + BioEdit.TextStyle = BioEdit.TextStyle.Clone(); + BioEdit.TextStyle.Color = UIStyle.Current.Text; + BioEdit.TextStyle.Size = 12; + BioEdit.MaxLines = 7; + Add(BioEdit); + + UpdateType(); + + SetTab(0); + } + + public void SetTab(int tab) + { + var sim = tab == 0; + var per = tab == 1; + var bio = tab == 2; + + SimGenderTitle.Visible = sim; + SimAgeTitle.Visible = sim; + SimSkinTitle.Visible = sim; + SimMaleBtn.Visible = sim; + SimFemaleBtn.Visible = sim; + SimAdultBtn.Visible = sim; + SimChildBtn.Visible = sim; + SimSkinLgtBtn.Visible = sim; + SimSkinMedBtn.Visible = sim; + SimSkinDrkBtn.Visible = sim; + SimRandomBtn.Visible = sim; + + PerNeatTitle.Visible = per; + PerOutgoingTitle.Visible = per; + PerActiveTitle.Visible = per; + PerPlayfulTitle.Visible = per; + PerNiceTitle.Visible = per; + for (int i = 0; i < 5; i++) + { + Personalities[i].Visible = per; + } + + BioTitle.Visible = bio; + BioBG.Visible = bio; + BioEdit.Visible = bio; + + SimTabButton.Selected = sim; + PersonalityTabButton.Selected = per; + BioTabButton.Selected = bio; + + ActiveTab = tab; + } + + private void SimRandomBtn_OnButtonClick(UIElement button) + { + OnRandom?.Invoke(); + } + + public int AllowedPoints = 25; + public int TotalPoints = 0; + + public void UpdateTotalPoints() + { + TotalPoints = 0; + for (int i = 0; i < 5; i++) + { + TotalPoints += Personalities[i].Points; + } + } + + public override void Draw(UISpriteBatch batch) + { + //draw backgrounds + var px = TextureGenerator.GetPxWhite(GameFacade.GraphicsDevice); + DrawLocalTexture(batch, px, null, new Vector2(90, 0), new Vector2(410, 70), UIStyle.Current.Bg); + DrawLocalTexture(batch, px, null, new Vector2(90, 75), new Vector2(410, 196), UIStyle.Current.Bg); + + //tab bgs + DrawLocalTexture(batch, px, null, SimTabButton.Position, new Vector2(85, 85), UIStyle.Current.Bg); + DrawLocalTexture(batch, px, null, PersonalityTabButton.Position, new Vector2(85, 85), UIStyle.Current.Bg); + DrawLocalTexture(batch, px, null, BioTabButton.Position, new Vector2(85, 85), UIStyle.Current.Bg); + base.Draw(batch); + + if (ActiveTab == 1) + { + //personality tab. draw remaining points + for (int i = 0; i < AllowedPoints; i++) + { + var col = (i < AllowedPoints-TotalPoints) ? UIStyle.Current.SkillActive : UIStyle.Current.SkillInactive; + DrawLocalTexture(batch, PersonalityTex, null, new Vector2(490, 255-i*7), Vector2.One, col, (float)(Math.PI / 2)); + } + } + } + + private void InitLabel(UILabel label, int fontSize) + { + label.CaptionStyle = label.CaptionStyle.Clone(); + label.CaptionStyle.Size = fontSize; + label.CaptionStyle.Color = UIStyle.Current.Text; + Add(label); + } + + private void ChangeType(int i, char targ) + { + var temp = AType.ToCharArray(); + temp[i] = targ; + AType = new string(temp); + UpdateType(); + } + + private void ChangeSkin(string skin) + { + SkinType = skin; + UpdateType(); + } + + public void UpdateType() + { + SimAdultBtn.Selected = AType[1] == 'a'; + SimChildBtn.Selected = AType[1] == 'c'; + + SimMaleBtn.Selected = AType[0] == 'm'; + SimFemaleBtn.Selected = AType[0] == 'f'; + + SimSkinLgtBtn.Selected = SkinType == "lgt"; + SimSkinMedBtn.Selected = SkinType == "med"; + SimSkinDrkBtn.Selected = SkinType == "drk"; + + OnCollectionChange?.Invoke(); + } + } + + public class UICASPersonalityBar : UIElement + { + private Texture2D BarItem; + public int Points; + private UIMouseEventRef ClickHandler; + private bool MouseOn; + private UISimCASPanel CAS; + + public UICASPersonalityBar(UISimCASPanel cas) + { + BarItem = Content.Get().CustomUI.Get("bar.png").Get(GameFacade.GraphicsDevice); + ClickHandler = ListenForMouse(new Rectangle(-20, -6, 274, 32), new UIMouseEvent(MouseEvt)); + CAS = cas; + } + + public void MouseEvt(UIMouseEventType type, UpdateState state) + { + if (type == UIMouseEventType.MouseDown) MouseOn = true; + else if (type == UIMouseEventType.MouseUp) MouseOn = false; + } + + public override void Update(UpdateState state) + { + base.Update(state); + if (MouseOn) + { + //how much allowance do we have on this specifically + var allowance = CAS.AllowedPoints - (CAS.TotalPoints - Points); + var newPTs = Math.Max(0, Math.Min(allowance, (int)Math.Ceiling(GlobalPoint(state.MouseState.Position.ToVector2()).X / 24))); + if (newPTs != Points) + { + HITVM.Get().PlaySoundEvent(UISounds.CreateCharacterPersonality); + Points = newPTs; + CAS.UpdateTotalPoints(); + } + } + } + + public override void Draw(UISpriteBatch batch) + { + if (!Visible) return; + for (int i=0; i<10; i++) + { + var col = (i < Points) ? UIStyle.Current.SkillActive : UIStyle.Current.SkillInactive; + DrawLocalTexture(batch, BarItem, new Rectangle(0, 0, 10, 20), new Vector2(i * 24, 0), Vector2.One, col); + DrawLocalTexture(batch, BarItem, new Rectangle(20, 0, 10, 20), new Vector2(i * 24 + 10, 0), Vector2.One, col); + } + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/Catalog/UICatalogItem.cs b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/Catalog/UICatalogItem.cs new file mode 100644 index 0000000..cd6960b --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/Catalog/UICatalogItem.cs @@ -0,0 +1,105 @@ +using FSO.Client; +using FSO.Client.UI.Controls; +using FSO.Client.UI.Framework; +using FSO.Content; +using FSO.Files.Formats.IFF.Chunks; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Simitone.Client.UI.Controls; +using Simitone.Client.UI.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Simitone.Client.UI.Panels.LiveSubpanels.Catalog +{ + public class UICatalogItem : UITSContainer + { + public static Dictionary IconCache = new Dictionary(); + public Texture2D BG; + public Texture2D Icon; + public Texture2D Outline; + public bool Outlined; + + public UILabel PriceLabel; + private UIBuyBrowsePanel BudgetProvider; + + public override void Draw(UISpriteBatch SBatch) + { + DrawLocalTexture(SBatch, BG, null, new Vector2(BG.Width-90, BG.Height-105) / -2, Vector2.One, new Color(104, 164, 184, 255)); + var iconSize = 55f; + if (Icon != null) + { + + if (Icon.Width / (float)Icon.Height < 1.1f || Icon.Width == 127 || Icon.Width == 128) + { + iconSize = 77.7f; + var scale = iconSize/(float)Math.Sqrt(Icon.Width * Icon.Width + Icon.Height * Icon.Height); + DrawLocalTexture(SBatch, Icon, new Rectangle(0, 0, Icon.Width, Icon.Height), new Vector2((Icon.Width*scale-90) / -2, (Icon.Height*scale-105) / -2), new Vector2(scale)); + } + else DrawLocalTexture(SBatch, Icon, new Rectangle(0, 0, Icon.Width / 2, Icon.Height), new Vector2((iconSize-90) / -2, (iconSize- 105) / -2), new Vector2(iconSize / Icon.Height, iconSize / Icon.Height)); + } + + if (Outlined) DrawLocalTexture(SBatch, Outline, null, new Vector2(Outline.Width - 90, Outline.Height - 105) / -2, Vector2.One, UIStyle.Current.ActiveSelection); + base.Draw(SBatch); + } + + public UICatalogItem(UICatalogElement elem, UIBuyBrowsePanel budgetProvider) + { + BG = Content.Get().CustomUI.Get("pswitch_icon_bg.png").Get(GameFacade.GraphicsDevice); + Icon = (elem.Special?.Res != null) ? elem.Special.Res.GetIcon(elem.Special.ResID) : GetObjIcon(elem.Item.GUID); + Outline = Content.Get().CustomUI.Get("pswitch_icon_sel.png").Get(GameFacade.GraphicsDevice); + + PriceLabel = new UILabel(); + PriceLabel.Alignment = TextAlignment.Center | TextAlignment.Middle; + PriceLabel.Position = new Vector2(0, 110); + PriceLabel.Size = new Vector2(90, 1); + PriceLabel.CaptionStyle = PriceLabel.CaptionStyle.Clone(); + PriceLabel.CaptionStyle.Color = UIStyle.Current.Text; + PriceLabel.CaptionStyle.Size = 14; + PriceLabel.Caption = "§" + elem.Item.Price.ToString(); + Add(PriceLabel); + + BudgetProvider = budgetProvider; + } + + public override void Selected() + { + Outlined = true; + BudgetProvider.Selected(ItemID); + } + + public override void Deselected() + { + Outlined = false; + } + + public Texture2D GetObjIcon(uint GUID) + { + if (!IconCache.ContainsKey(GUID)) + { + var obj = Content.Get().WorldObjects.Get(GUID); + if (obj == null) + { + IconCache[GUID] = null; + return null; + } + var bmp = obj.Resource.Get(obj.OBJ.CatalogStringsID); + if (bmp != null) IconCache[GUID] = bmp.GetTexture(GameFacade.GraphicsDevice); + else IconCache[GUID] = null; + } + return IconCache[GUID]; + } + + public static void ClearIconCache() + { + foreach (var item in IconCache) + { + item.Value?.Dispose(); + } + IconCache.Clear(); + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIButtonSubpanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIButtonSubpanel.cs new file mode 100644 index 0000000..1ff576d --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIButtonSubpanel.cs @@ -0,0 +1,76 @@ +using FSO.Client; +using FSO.Client.UI.Controls; +using FSO.Client.UI.Framework; +using FSO.Content; +using Microsoft.Xna.Framework; +using Simitone.Client.UI.Controls; +using Simitone.Client.UI.Model; +using Simitone.Client.UI.Screens; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Simitone.Client.UI.Panels.LiveSubpanels +{ + public class UIButtonSubpanel : UISubpanel + { + private float _InitShow; + public float InitShow + { + get + { + return _InitShow; + } + set + { + Invalidate(); + _InitShow = value; + } + } + public UIButtonSubpanel(TS1GameScreen game, UICatFunc[] funcs) : base(game) + { + for (int i = 0; i < funcs.Length; i++) + { + var func = funcs[i]; + + var label = new UILabel(); + label.Caption = func.Name; + label.Alignment = TextAlignment.Middle | TextAlignment.Center; + label.Wrapped = true; + label.Position = new Vector2(-77, 106); + label.Size = new Vector2(120, 1); + label.CaptionStyle = label.CaptionStyle.Clone(); + label.CaptionStyle.Size = 12; + label.CaptionStyle.Color = UIStyle.Current.Text; + Add(label); + + var subbutton = new UICatButton(Content.Get().CustomUI.Get(func.IconName).Get(GameFacade.GraphicsDevice)); + subbutton.OnButtonClick += (btn) => { func.Func(); }; + subbutton.Position = new Vector2(-50, 16); + Add(subbutton); + + GameFacade.Screens.Tween.To(label, 0.5f, new Dictionary() { { "X", 50 + i * 120f - 27 } }, TweenQuad.EaseOut); + GameFacade.Screens.Tween.To(subbutton, 0.5f, new Dictionary() { { "X", 50 + i * 120f } }, TweenQuad.EaseOut); + GameFacade.Screens.Tween.To(this, 0.5f, new Dictionary() { { "InitShow", 1f } }, TweenQuad.EaseOut); + } + + InitShow = InitShow; + } + } + + public class UICatFunc + { + public string Name; + public string IconName; + public Action Func; + + public UICatFunc(string name, string iconName, Action func) + { + Name = name; + IconName = iconName; + Func = func; + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs new file mode 100644 index 0000000..e292e4f --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/LiveSubpanels/UIBuyBrowsePanel.cs @@ -0,0 +1,748 @@ +using FSO.Client.UI.Controls.Catalog; +using FSO.Content; +using FSO.Content.Interfaces; +using Microsoft.Xna.Framework; +using Simitone.Client.UI.Controls; +using Simitone.Client.UI.Screens; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FSO.Common.Rendering.Framework.Model; +using FSO.Client.UI.Framework; +using FSO.Client.UI.Controls; +using FSO.Client; +using Simitone.Client.UI.Model; +using FSO.Client.UI.Panels.LotControls; +using FSO.Client.UI.Model; +using FSO.LotView.Model; + +namespace Simitone.Client.UI.Panels.LiveSubpanels +{ + public class UIBuyBrowsePanel : UISubpanel + { + public UITouchScroll CatContainer; + public List FullCategory; + public IEnumerable FilterCategory; + public UICatalogMode Mode; + public List SelButtons = new List(); + public List SelLabels = new List(); + public sbyte Category; + public bool ChoosingSub; + public int ItemID = -1; + + public static int[] RemapString = new int[] + { + 0, //seat + 1, //surf + 4, // + 3, + 5, + 2, + 7, + 6 + }; + + public static List> Categories; + public static string[][] CatIcons = new string[][] + { + new string[] + { + "seat_dine", + "seat_loun", + "seat_sofa", + "seat_beds" + }, + new string[] + { + "surf_count", + "surf_tabl", + "surf_endt", + "surf_desk" + }, + new string[] + { + "appl_stov", + "appl_frig", + "appl_smal", + "appl_larg" + }, + new string[] + { + "elec_ent", + "elec_vide", + "elec_audi", + "elec_phon" + }, + + new string[] + { + "plum_toil", + "plum_show", + "plum_sink", + "plum_hott" + }, + + new string[] + { + "deco_pain", + "deco_scul", + "deco_rugs", + "deco_plan" + }, + new string[] + { + "misc_recr", + "misc_know", + "misc_crea", + "misc_ward", + "misc_pets", + "misc_pets", + "misc_magi", + }, + new string[] + { + "ligh_tabl", + "ligh_stan", + "ligh_wall", + "ligh_hang" + }, + }; + + public static List> BuildCategories = new List>() + { + new List() //architecture + { + new UICatalogSubcat() { MaskBit = 7+6, StrTable = 139, StrInd = 2}, //wall + new UICatalogSubcat() { MaskBit = 7+8, StrTable = 139, StrInd = 3}, //wallpaper + new UICatalogSubcat() { MaskBit = 7+9, StrTable = 139, StrInd = 7}, //floor + new UICatalogSubcat() { MaskBit = 7+10, StrTable = 139, StrInd = 10}, //roof + }, + new List() //outdoors + { + new UICatalogSubcat() { MaskBit = 7+4, StrTable = 139, StrInd = 6}, //trees + new UICatalogSubcat() { MaskBit = 7+11, StrTable = 139, StrInd = 0}, //terrain + new UICatalogSubcat() { MaskBit = 7+7, StrTable = 139, StrInd = 1}, //water + }, + new List() //objects + { + new UICatalogSubcat() { MaskBit = 7+1, StrTable = 139, StrInd = 8}, //door + new UICatalogSubcat() { MaskBit = 7+2, StrTable = 139, StrInd = 9}, //window + new UICatalogSubcat() { MaskBit = 7+3, StrTable = 139, StrInd = 4}, //staircase + new UICatalogSubcat() { MaskBit = 7+5, StrTable = 139, StrInd = 5}, //fireplaces + }, + }; + + public static string[][] BuildIcons = new string[][] + { + new string[] + { + "build_wall", + "build_walp", + "build_flor", + "build_roof", + }, + new string[] + { + "build_tree", + "build_terr", + "build_watr", + }, + new string[] + { + "build_door", + "build_wind", + "build_stai", + "build_fire" + }, + }; + + static UIBuyBrowsePanel() + { + Categories = new List>(); + + for (int i=0; i<8; i++) + { + //init buy categories. + var cat = new List(); + var modi = RemapString[i]; + for (int j=0; j<4; j++) + { + cat.Add(new UICatalogSubcat() + { + StrTable = 200 + modi, + MaskBit = j, + StrInd = j + }); + } + + if (i == 6) + { + cat.Add(new UICatalogSubcat() + { + StrTable = 210, + MaskBit = 5, + StrInd = 3, //pets + }); + + cat.Add(new UICatalogSubcat() + { + StrTable = 210, + MaskBit = 6, + StrInd = 4, //magic + }); + } + + cat.Add(new UICatalogSubcat() + { + StrTable = 210, + MaskBit = 7, + StrInd = 1, //other + }); + + cat.Add(new UICatalogSubcat() + { + StrTable = 210, + MaskBit = 8, + StrInd = 2, //all + }); + Categories.Add(cat); + } + } + + public bool HoldingEvents; + + public UIBuyBrowsePanel(TS1GameScreen screen, sbyte category, UICatalogMode mode) : base(screen) { + CatContainer = new UITouchScroll(() => FilterCategory?.Count() ?? 0, CatalogElemProvider); + CatContainer.ItemWidth = 90; + CatContainer.DrawBounds = false; + CatContainer.Margin = 15; + CatContainer.SetScroll(-15); + CatContainer.Size = new Vector2(775, 128); + Category = category; + + Add(CatContainer); + Mode = mode; + GameResized(); + + InitCategory(category, false); + + screen.LotControl.ObjectHolder.OnPickup += ObjectHolder_OnPickup; + screen.LotControl.ObjectHolder.OnPutDown += ObjectHolder_OnPutDown; + screen.LotControl.ObjectHolder.OnDelete += ObjectHolder_OnDelete; + HoldingEvents = true; + } + + private void ObjectHolder_OnDelete(UIObjectSelection holding, UpdateState state) + { + Game.Frontend.MainPanel.SetSubpanelPickup(1f); + Game.LotControl.QueryPanel.Active = false; + ItemID = -1; + } + + private void ObjectHolder_OnPutDown(UIObjectSelection holding, UpdateState state) + { + Game.LotControl.QueryPanel.Active = false; + Game.Frontend.MainPanel.SetSubpanelPickup(1f); + if (ItemID != -1) + { + if (!holding.IsBought && (state.ShiftDown)) + { + //place another + var prevDir = holding.Dir; + Selected(ItemID); + Game.LotControl.QueryPanel.SetShown(false); + Game.LotControl.ObjectHolder.Holding.Dir = prevDir; + } + else + { + ItemID = -1; + } + } + } + + private void ObjectHolder_OnPickup(UIObjectSelection holding, UpdateState state) + { + Game.LotControl.PickupPanel.SetInfo(Game.LotControl.vm, holding.Group.BaseObject); + Game.Frontend.MainPanel.SetSubpanelPickup(0f); + } + + private void RemoveEvents() + { + if (HoldingEvents) + { + HoldingEvents = false; + Game.LotControl.ObjectHolder.OnPickup -= ObjectHolder_OnPickup; + Game.LotControl.ObjectHolder.OnPutDown -= ObjectHolder_OnPutDown; + Game.LotControl.ObjectHolder.OnDelete -= ObjectHolder_OnDelete; + Game.LotControl.QueryPanel.Active = false; + Game.Frontend.MainPanel.SetSubpanelPickup(1f); + + if (Game.LotControl.CustomControl != null) + { + Game.LotControl.CustomControl.Release(); + Game.LotControl.CustomControl = null; + } + + if (Game.LotControl.ObjectHolder.Holding != null) + { + //delete object that hasn't been placed yet + //TODO: all holding objects should obviously just be ghosts. + //Holder.Holding.Group.Delete(vm.Context); + Game.LotControl.ObjectHolder.ClearSelected(); + } + } + } + + public override void Kill() + { + RemoveEvents(); + + base.Kill(); + } + + public void Selected(int itemID) + { + var holder = Game.LotControl.ObjectHolder; + var control = Game.LotControl; + holder.ClearSelected(); + var item = FilterCategory.ElementAt(itemID); + + //todo: check if over budget? + + // if (OldSelection != -1) Catalog.SetActive(OldSelection, false); + //Catalog.SetActive(selection, true); + + if (control.CustomControl != null) + { + control.CustomControl.Release(); + control.CustomControl = null; + } + + if (item.Special != null) + { + var res = item.Special.Res; + var resID = item.Special.ResID; + if (res != null && res.GetName(resID) != "") + { + Game.LotControl.QueryPanel.SetInfo(res.GetThumb(resID), res.GetName(resID), res.GetDescription(resID), res.GetPrice(resID), res.DoDispose()); + Game.LotControl.QueryPanel.Mode = 1; + //QueryPanel.Tab = 0; + Game.LotControl.QueryPanel.Active = true; + Game.LotControl.QueryPanel.SetShown(true); + } + control.CustomControl = (UICustomLotControl)Activator.CreateInstance(item.Special.Control, control.vm, control.World, control, item.Special.Parameters); + } + else + { + var BuyItem = control.vm.Context.CreateObjectInstance(item.Item.GUID, LotTilePos.OUT_OF_WORLD, Direction.NORTH, holder.UseNet); + if (BuyItem.Objects.Count != 0) + { + Game.LotControl.QueryPanel.SetInfo(Game.LotControl.vm, BuyItem.Objects[0], false); + Game.LotControl.QueryPanel.Mode = 1; + //QueryPanel.Tab = 0; + Game.LotControl.QueryPanel.Active = true; + Game.LotControl.QueryPanel.SetShown(true); + holder.SetSelected(BuyItem); + } + } + + ItemID = itemID; + } + + public override void Removed() + { + RemoveEvents(); + base.Removed(); + //Catalog.UICatalogItem.ClearIconCache(); //might want to be careful here... + } + + public void Reset() + { + GameFacade.Screens.Tween.To(CatContainer, 0.5f, new Dictionary() { { "Opacity", 0f } }, TweenQuad.EaseOut); + InitCategory(Category, false); + } + + public void InitCategory(sbyte category, bool build) + { + //start by populating with entries from the catalog + if (!build) ((UIMainPanel)Parent)?.Switcher?.MainButton?.RestoreImage(); + var catalog = Content.Get().WorldCatalog; + + var items = catalog.GetItemsByCategory(category); + + FullCategory = items.Select(x => new UICatalogElement() + { + Item = x, + CalcPrice = (int)x.Price + }).ToList(); + + //pull from other categories + + if (category == 15) AddWallpapers(); + if (category == 14 || category == 16) AddFloors(category); + if (category == 17) AddRoofs(); + if (category == 18) AddTerrainTools(); + + FullCategory = FullCategory.OrderBy(x => (int)x.Item.Price).ToList(); + if (category == 13) AddWallStyles(); + //if we're not build mode, init the subcategory selection + if (build) return; + + foreach (var btn in SelButtons) Remove(btn); + foreach (var label in SelLabels) Remove(label); + SelButtons.Clear(); + SelLabels.Clear(); + + ChoosingSub = true; + + var cats = (Mode == UICatalogMode.Build)?BuildCategories[category]:Categories[category]; + + var boff = CatContainer.Size.X/(cats.Count + 0.5f) / 2f; + + for (int i=0; i { InitSubcategory(cat); }; + subbutton.Position = new Vector2(boff * (1.5f + i * 2) - (65 / 2), 16); + SelButtons.Add(subbutton); + Add(subbutton); + } + + //InitSubcategory(0); + } + + private void AddFloors(sbyte category) + { + var res = new UICatalogFloorResProvider(); + + var floors = Content.Get().WorldFloors.List(); + + for (int i = 0; i < floors.Count; i++) + { + var floor = (FloorReference)floors[i]; + + if ((category == 14) != (floor.ID >= 65534)) continue; + FullCategory.Insert(0, new UICatalogElement + { + Item = new ObjectCatalogItem() + { + Name = floor.Name, + Category = category, + Price = (uint)floor.Price, + }, + Special = new UISpecialCatalogElement + { + Control = typeof(UIFloorPainter), + ResID = floor.ID, + Res = res, + Parameters = new List { (int)floor.ID } //pattern + } + }); + } + } + + private void AddWallpapers() + { + var res = new UICatalogWallpaperResProvider(); + + var walls = Content.Get().WorldWalls.List(); + + for (int i = 0; i < walls.Count; i++) + { + var wall = (WallReference)walls[i]; + FullCategory.Insert(0, new UICatalogElement + { + Item = new ObjectCatalogItem() + { + Name = wall.Name, + Category = 15, + Price = (uint)wall.Price, + }, + Special = new UISpecialCatalogElement + { + Control = typeof(UIWallPainter), + ResID = wall.ID, + Res = res, + Parameters = new List { (int)wall.ID } //pattern + } + }); + } + } + + private void AddRoofs() + { + var res = new UICatalogRoofResProvider(); + + var total = Content.Get().WorldRoofs.Count; + + for (int i = 0; i < total; i++) + { + sbyte category = 17; + FullCategory.Insert(0, new UICatalogElement + { + Item = new ObjectCatalogItem() + { + Name = "", + Category = category, + Price = 0, + }, + Special = new UISpecialCatalogElement + { + Control = typeof(UIRoofer), + ResID = (uint)i, + Res = res, + Parameters = new List { i } //pattern + } + }); + } + } + + public static short[] WallStyleIDs = + { + 0x1, //wall + 0x2, //picket fence + 0xD, //iron fence + 0xC, //privacy fence + 0xE //banisters + }; + + public static short[] WallStylePatterns = + { + 0, //wall + 248, //picket fence + 250, //iron fence + 249, //privacy fence + 251, //banisters + }; + + private void AddWallStyles() + { + var res = new UICatalogWallResProvider(); + + for (int i = 0; i < WallStyleIDs.Length; i++) + { + var walls = Content.Get().WorldWalls; + var style = walls.GetWallStyle((ulong)WallStyleIDs[i]); + FullCategory.Insert(i, new UICatalogElement + { + Item = new ObjectCatalogItem() + { + Name = style.Name, + Category = 13, + Price = (uint)style.Price, + }, + Special = new UISpecialCatalogElement + { + Control = typeof(UIWallPlacer), + ResID = (ulong)WallStyleIDs[i], + Res = res, + Parameters = new List { WallStylePatterns[i], WallStyleIDs[i] } //pattern, style + } + }); + } + } + + private void AddTerrainTools() + { + var res = new UICatalogWallResProvider(); + + FullCategory.Insert(0, new UICatalogElement + { + Item = new ObjectCatalogItem() + { + Name = "Raise/Lower Terrain", + Category = 18, + Price = 1, + }, + Special = new UISpecialCatalogElement + { + Control = typeof(UITerrainRaiser), + ResID = 0, + Res = new UICatalogTerrainResProvider(), + Parameters = new List { } + } + }); + + FullCategory.Insert(0, new UICatalogElement + { + Item = new ObjectCatalogItem() + { + Name = "Flatten Terrain", + Category = 18, + Price = 1, + }, + Special = new UISpecialCatalogElement + { + Control = typeof(UITerrainFlatten), + ResID = 1, + Res = new UICatalogTerrainResProvider(), + Parameters = new List { } + } + }); + + FullCategory.Insert(0, new UICatalogElement + { + Item = new ObjectCatalogItem() + { + Name = "Grass Tool", + Category = 18, + Price = 1, + }, + Special = new UISpecialCatalogElement + { + Control = typeof(UIGrassPaint), + ResID = 2, + Res = new UICatalogTerrainResProvider(), + Parameters = new List { } + } + }); + } + + public override void GameResized() + { + CatContainer.Size = new Vector2(UIScreen.Current.ScreenWidth - 342, 128); + if (ChoosingSub) Reset(); + base.GameResized(); + } + + public override void Update(UpdateState state) + { + Invalidate(); + var first = SelButtons.FirstOrDefault(); + if (first != null && first.Opacity == 0) + { + foreach (var btn in SelButtons) Remove(btn); + foreach (var label in SelLabels) Remove(label); + SelButtons.Clear(); + SelLabels.Clear(); + } + base.Update(state); + } + + public UITSContainer CatalogElemProvider(int index) + { + var elem = new Catalog.UICatalogItem(FilterCategory.ElementAt(index), this); + return elem; + } + + public byte GetSubsort(ObjectCatalogItem item) + { + switch (Mode) + { + case UICatalogMode.Downtown: + return item.DowntownSort; + case UICatalogMode.Community: + return item.CommunitySort; + case UICatalogMode.Vacation: + return item.VacationSort; + case UICatalogMode.Studiotown: + return item.StudiotownSort; + case UICatalogMode.Magictown: + return item.MagictownSort; + default: + return item.Subsort; + } + } + + public void InitSubcategory(UICatalogSubcat cat) + { + var index = cat.MaskBit; + if (!ChoosingSub) return; + ChoosingSub = false; + ((UIMainPanel)Parent).Switcher.Close(); + ((UIMainPanel)Parent).Switcher.MainButton.ReplaceImage(Content.Get().CustomUI.Get("cat_" + cat.IconName + ".png").Get(GameFacade.GraphicsDevice)); + + foreach (var btn in SelButtons) + { + GameFacade.Screens.Tween.To(btn, 0.5f, new Dictionary() { { "Opacity", 0f } }, TweenQuad.EaseOut); + } + foreach (var label in SelLabels) + { + GameFacade.Screens.Tween.To(label, 0.5f, new Dictionary() { { "Opacity", 0f } }, TweenQuad.EaseOut); + } + + CatContainer.Opacity = 0f; + GameFacade.Screens.Tween.To(CatContainer, 0.5f, new Dictionary() { { "Opacity", 1f } }, TweenQuad.EaseOut); + + if (Mode == UICatalogMode.Build) + { + InitCategory((sbyte)index, true); + FilterCategory = FullCategory; + } + else if (index == 8) + { + FilterCategory = FullCategory.Where(x => (GetSubsort(x.Item)) > 0); + } + else + { + var mask = 1 << index; + FilterCategory = FullCategory.Where(x => (GetSubsort(x.Item) & mask) > 0); + } + CatContainer.Reset(); + } + } + + public enum UICatalogMode + { + Normal, + Downtown, + Community, + Vacation, + Studiotown, + Magictown, + Build + } + + public class UICatalogElement + { + public ObjectCatalogItem Item; + public int CalcPrice; + public UISpecialCatalogElement Special; + } + + public class UISpecialCatalogElement + { + public Type Control; + public ulong ResID; + public UICatalogResProvider Res; + public List Parameters; + } + + public class UICatalogSubcat + { + public int StrTable; + public int StrInd; + public int MaskBit; + public string IconName; + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/LotControls/UIArchTouchHelper.cs b/Client/Simitone/Simitone.Client/UI/Panels/LotControls/UIArchTouchHelper.cs new file mode 100644 index 0000000..d4c5e2a --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/LotControls/UIArchTouchHelper.cs @@ -0,0 +1,100 @@ +using FSO.Client; +using FSO.Client.UI.Framework; +using FSO.Content; +using Simitone.Client.UI.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FSO.Common.Rendering.Framework.Model; +using Microsoft.Xna.Framework; +using FSO.Client.UI.Panels.LotControls; +using Microsoft.Xna.Framework.Graphics; +using FSO.Common; + +namespace Simitone.Client.UI.Panels.LotControls +{ + public class UIArchTouchHelper : UIContainer + { + public UITwoStateButton ClickButton; + public UITwoStateButton ShiftClickButton; + public UITwoStateButton CtrlClickButton; + + public UITwoStateButton RotateCWButton; + public UITwoStateButton RotateCCWButton; + private Texture2D Cross; + + public UILotControl Owner; + public UpdateState LastState; + + public UIArchTouchHelper(UILotControl parent) + { + Owner = parent; + var ui = Content.Get().CustomUI; + var gd = GameFacade.GraphicsDevice; + + ClickButton = new UITwoStateButton(ui.Get("touch_tool.png").Get(gd)); + Add(ClickButton); + ClickButton.OnButtonDown += (b) => Owner.SimulateMD(LastState); + ClickButton.OnButtonClick += (b) => Owner.SimulateMU(LastState); + ShiftClickButton = new UITwoStateButton(ui.Get("touch_tools.png").Get(gd)); + Add(ShiftClickButton); + ShiftClickButton.OnButtonDown += (b) => { Owner.AddModifier(UILotControlModifiers.SHIFT); Owner.SimulateMD(LastState); }; + ShiftClickButton.OnButtonClick += (b) => { Owner.SimulateMU(LastState); Owner.RemoveModifier(UILotControlModifiers.SHIFT); }; + CtrlClickButton = new UITwoStateButton(ui.Get("touch_toolc.png").Get(gd)); + Add(CtrlClickButton); + CtrlClickButton.OnButtonDown += (b) => { Owner.AddModifier(UILotControlModifiers.CTRL); Owner.SimulateMD(LastState); }; + CtrlClickButton.OnButtonClick += (b) => { Owner.SimulateMU(LastState); Owner.RemoveModifier(UILotControlModifiers.CTRL); }; + + RotateCWButton = new UITwoStateButton(ui.Get("touch_rotcw.png").Get(gd)); + Add(RotateCWButton); + RotateCWButton.OnButtonClick += (b) => { parent.ObjectHolder.RotateObject(2); }; + RotateCCWButton = new UITwoStateButton(ui.Get("touch_rotccw.png").Get(gd)); + RotateCCWButton.OnButtonClick += (b) => { parent.ObjectHolder.RotateObject(-2); }; + Add(RotateCCWButton); + + Cross = ui.Get("touch_cross.png").Get(gd); + + GameResized(); + } + + public override void GameResized() + { + base.GameResized(); + ClickButton.Position = new Vector2(25, 25); + ShiftClickButton.Position = new Vector2(25, UIScreen.Current.ScreenHeight - 255); + CtrlClickButton.Position = new Vector2(UIScreen.Current.ScreenWidth - 100, UIScreen.Current.ScreenHeight - 255); + + RotateCWButton.Position = new Vector2(25, UIScreen.Current.ScreenHeight - 255); + RotateCCWButton.Position = new Vector2(UIScreen.Current.ScreenWidth - 100, UIScreen.Current.ScreenHeight - 255); + } + + public override void Update(UpdateState state) + { + LastState = state; + if (Owner.LiveMode || !FSOEnvironment.SoftwareKeyboard) Visible = false; + else + { + var custom = (Owner.CustomControl != null); + ShiftClickButton.Visible = custom; + CtrlClickButton.Visible = custom; + + RotateCCWButton.Visible = !custom; + RotateCWButton.Visible = !custom; + + Visible = custom || Owner.ObjectHolder.Holding != null; + } + base.Update(state); + } + + public override void Draw(UISpriteBatch batch) + { + if (!Visible) return; + base.Draw(batch); + //DrawLocalTexture(batch, Cross, new Vector2(UIScreen.Current.ScreenWidth - 98, UIScreen.Current.ScreenHeight - 98)/2); + } + + + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UI3DThumb.cs b/Client/Simitone/Simitone.Client/UI/Panels/UI3DThumb.cs new file mode 100644 index 0000000..3c811f3 --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/UI3DThumb.cs @@ -0,0 +1,123 @@ +using FSO.Client; +using FSO.Common.Rendering.Framework; +using FSO.Common.Rendering.Framework.Camera; +using FSO.Content; +using FSO.LotView.Components; +using FSO.LotView.Debug; +using FSO.SimAntics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Simitone.Client.UI.Panels +{ + public class UI3DThumb + { + public _3DTargetScene Scene; + public BasicCamera Camera; + public Texture2D Tex + { + get { return Scene.Target; } + } + + public float RotationSpeed = 0.20f; + public float RotationX = -(float)Math.PI; + public Vector3 Ctr; + public float Size; + + public List Comp3D; + public UI3DThumb(VMEntity ent) + { + Camera = new BasicCamera(GameFacade.GraphicsDevice, new Vector3(3, 1, 0), new Vector3(0, 0, 0), new Vector3(0, 1, 0)); + Camera.NearPlane = 0.001f; + Scene = new _3DTargetScene(GameFacade.GraphicsDevice, Camera, new Point(150, 150), 0); + Scene.Initialize(GameFacade.Scenes); + + if (Comp3D != null) + { + foreach (var e in Comp3D) + { + e.Dispose(); + Scene.Remove(e); + } + } + Comp3D = new List(); + + BoundingBox? total = null; + var pos = ent.MultitileGroup.GetBasePositions(); + var i = 0; + foreach (var obj in ent.MultitileGroup.Objects) + { + var c = new Debug3DDGRPComponent(); + var dgrp = ((ObjectComponent)obj.WorldUI).DGRP; + c.Mesh = (dgrp == null) ? null : Content.Get().RCMeshes.Get(dgrp, obj.Object.OBJ); //new DGRP3DMesh(((ObjectComponent)obj.WorldUI).DGRP, obj.Object.OBJ, GameFacade.GraphicsDevice, null); + Scene.Add(c); + if (c.Mesh == null) continue; + + var vp = pos[i++]; + c.Position = new Vector3((vp.X - 0.5f), vp.Z, (vp.Y - 0.5f)); + if (total == null) total = OffsetBox(c.Mesh.Bounds ?? new BoundingBox(), c.Position); + else total = BoundingBox.CreateMerged(total.Value, OffsetBox(c.Mesh.Bounds ?? new BoundingBox(), c.Position)); + c.Initialize(); + Comp3D.Add(c); + } + + if (total != null) + { + Ctr = new Vector3((total.Value.Max.X + total.Value.Min.X) / 2, (total.Value.Max.Y + total.Value.Min.Y) / 2, (total.Value.Max.Z + total.Value.Min.Z) / 2); + var diag = total.Value.Max - total.Value.Min; + Size = diag.Length(); + } + } + + public BoundingBox OffsetBox(BoundingBox box, Vector3 off) + { + return new BoundingBox(box.Min + off, box.Max + off); + } + + public void RecalcBounds() + { + BoundingBox? total = null; + foreach (var c in Comp3D) + { + if (total == null) total = OffsetBox(c.Mesh.Bounds ?? new BoundingBox(), c.Position); + else total = BoundingBox.CreateMerged(total.Value, OffsetBox(c.Mesh.Bounds ?? new BoundingBox(), c.Position)); + } + + if (total != null) + { + Ctr = new Vector3((total.Value.Max.X + total.Value.Min.X) / 2, (total.Value.Max.Y + total.Value.Min.Y) / 2, (total.Value.Max.Z + total.Value.Min.Z) / 2); + var diag = total.Value.Max - total.Value.Min; + Size = diag.Length(); + } + } + + public void Draw() + { + RecalcBounds(); + RotationX += RotationSpeed; + RotationSpeed += (0.01f - RotationSpeed) / 20; + + var mat = Microsoft.Xna.Framework.Matrix.CreateRotationY(RotationX); + Camera.Position = Ctr + Vector3.Transform(new Vector3(4, 3, 0)*(0.1f + Size*0.2f), mat); + Camera.Target = Ctr + new Vector3(0, 0, 0); + var old = GameFacade.GraphicsDevice.BlendState; + GameFacade.GraphicsDevice.BlendState = BlendState.NonPremultiplied; + Scene.Draw(GameFacade.GraphicsDevice); + GameFacade.GraphicsDevice.BlendState = old; + } + + public void Dispose() + { + Scene.Dispose(); + if (Comp3D != null) + { + foreach (var e in Comp3D) e.Dispose(); + } + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UIModeSwitcher.cs b/Client/Simitone/Simitone.Client/UI/Panels/UIModeSwitcher.cs new file mode 100644 index 0000000..8942529 --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/UIModeSwitcher.cs @@ -0,0 +1,140 @@ +using FSO.Client; +using FSO.Client.UI.Controls; +using FSO.Client.UI.Framework; +using FSO.Content; +using Microsoft.Xna.Framework; +using Simitone.Client.UI.Controls; +using Simitone.Client.UI.Screens; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FSO.Common.Rendering.Framework.Model; + +namespace Simitone.Client.UI.Panels +{ + public class UIModeSwitcher : UIContainer + { + public UILiveButton LiveButton; + public UIElasticButton BuyButton; + public UIElasticButton BuildButton; + public UIElasticButton OptionButton; + + private UIButton[] ButtonOrder; + public Func OnModeClick; + public TS1GameScreen Game; + + public UIModeSwitcher(TS1GameScreen screen) + { + Game = screen; + var ui = Content.Get().CustomUI; + + var btn = new UILiveButton(screen); + btn.MotiveLevel = 0.5f; + btn.Position = Vector2.Zero; + btn.OnButtonClick += (b) => { SwitchMode(UIMainPanelMode.LIVE); }; + Add(btn); + LiveButton = btn; + + BuildButton = new UIElasticButton(ui.Get("mode_build.png").Get(GameFacade.GraphicsDevice)); + BuildButton.Position = btn.Position; + BuildButton.OnButtonClick += (b) => { SwitchMode(UIMainPanelMode.BUILD); }; + BuildButton.Opacity = 0; + Add(BuildButton); + + BuyButton = new UIElasticButton(ui.Get("mode_buy.png").Get(GameFacade.GraphicsDevice)); + BuyButton.Position = btn.Position; + BuyButton.OnButtonClick += (b) => { SwitchMode(UIMainPanelMode.BUY); }; + BuyButton.Opacity = 0; + Add(BuyButton); + + OptionButton = new UIElasticButton(ui.Get("mode_options.png").Get(GameFacade.GraphicsDevice)); + OptionButton.Position = btn.Position; + OptionButton.OnButtonClick += (b) => { SwitchMode(UIMainPanelMode.OPTIONS); }; + OptionButton.Opacity = 0; + Add(OptionButton); + + ButtonOrder = new UIButton[] + { + LiveButton, + BuyButton, + BuildButton, + OptionButton + }; + } + + public override void Update(UpdateState state) + { + base.Update(state); + foreach (var btn in ButtonOrder) + { + btn.Visible = btn.Opacity > 0; + } + } + + public void EndSwitch(UIMainPanelMode mode) + { + UIButton frontButton; + switch (mode) + { + case UIMainPanelMode.BUILD: + frontButton = BuildButton; break; + case UIMainPanelMode.BUY: + frontButton = BuyButton; break; + case UIMainPanelMode.OPTIONS: + frontButton = OptionButton; break; + default: + frontButton = LiveButton; break; + } + //become this mode + SendToFront(frontButton); + foreach (var button in ButtonOrder) + { + GameFacade.Screens.Tween.To(button, 0.5f, new Dictionary() { { "Y", 0 } }, TweenQuad.EaseOut); + GameFacade.Screens.Tween.To(button, 0.5f, new Dictionary() { { "Opacity", (button != frontButton)?0f:1f } }, TweenQuad.EaseOut); + } + } + + public void SwitchMode(UIMainPanelMode mode) + { + UIButton frontButton; + switch (mode) + { + case UIMainPanelMode.BUILD: + frontButton = BuildButton; break; + case UIMainPanelMode.BUY: + frontButton = BuyButton; break; + case UIMainPanelMode.OPTIONS: + frontButton = OptionButton; break; + default: + frontButton = LiveButton; break; + } + if (OnModeClick?.Invoke(mode) ?? true) + { + //switching mode. show the modes. + int i = 0; + foreach (var button in ButtonOrder) + { + if (button == LiveButton && Game?.LotControl.ActiveEntity == null) continue; + button.Visible = true; + GameFacade.Screens.Tween.To(button, 0.5f, new Dictionary() { { "Y", (-140)*(i++) } }, TweenQuad.EaseOut); + GameFacade.Screens.Tween.To(button, 0.5f, new Dictionary() { { "Opacity", 1 } }, TweenQuad.EaseOut); + } + } + else + { + //become this mode + //should happen as part of callback from main panel.. + /* + SendToFront(frontButton); + foreach (var button in ButtonOrder) + { + GameFacade.Screens.Tween.To(button, 0.5f, new Dictionary() { { "Y", 0 } }, TweenQuad.EaseOut); + GameFacade.Screens.Tween.To(button, 0.5f, new Dictionary() { { "Opacity", (button != frontButton) ? 0f : 1f } }, TweenQuad.EaseOut); + } + */ + } + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UINeighbourhoodSwitcher.cs b/Client/Simitone/Simitone.Client/UI/Panels/UINeighbourhoodSwitcher.cs new file mode 100644 index 0000000..c13c10e --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/UINeighbourhoodSwitcher.cs @@ -0,0 +1,78 @@ +using FSO.Client; +using FSO.Client.UI.Controls; +using FSO.Client.UI.Framework; +using FSO.Content; +using Simitone.Client.UI.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Simitone.Client.UI.Panels +{ + public class UINeighbourhoodSwitcher : UIContainer + { + public List LeftBtns = new List(); + public List RightBtns = new List(); + private UINeighborhoodSelectionPanel Panel; + private ushort Mode; + + public UINeighbourhoodSwitcher(UINeighborhoodSelectionPanel panel, ushort mode) + { + Panel = panel; + SetMode(mode); + } + + public void SetMode(ushort mode) + { + foreach (var btn in LeftBtns) Remove(btn); + foreach (var btn in RightBtns) Remove(btn); + LeftBtns.Clear(); RightBtns.Clear(); + + AddBtn(LeftBtns, "ngbh_cas.png", (btn) => GameController.EnterCAS()); + if (mode != 4) AddBtn(LeftBtns, "ngbh_back.png", (btn) => PopMode(4)); + + if (mode != 2) AddBtn(RightBtns, "ngbh_downt.png", (btn) => PopMode(2)); + if (mode != 3) AddBtn(RightBtns, "ngbh_vacat.png", (btn) => PopMode(3)); + if (mode != 5) AddBtn(RightBtns, "ngbh_studio.png", (btn) => PopMode(5)); + if (mode != 7) AddBtn(RightBtns, "ngbh_magic.png", (btn) => PopMode(7)); + Mode = mode; + + LayBtns(); + } + + public void PopMode(ushort mode) + { + Panel.PopulateScreen(mode); + SetMode(mode); + } + + private void LayBtns() + { + int i = 0; + foreach (var btn in LeftBtns) + { + btn.Position = new Microsoft.Xna.Framework.Vector2(17+48, 14+110*i + 48); + i++; + } + + i = 0; + foreach (var btn in RightBtns) + { + btn.Position = new Microsoft.Xna.Framework.Vector2(UIScreen.Current.ScreenWidth - (17+96) + 48, 14 + 110 * i + 48); + i++; + } + } + + private UIElasticButton AddBtn(List targ, string imgname, ButtonClickDelegate onClick) + { + var ui = Content.Get().CustomUI; + var btn = new UIElasticButton(ui.Get(imgname).Get(GameFacade.GraphicsDevice)); + btn.OnButtonClick += onClick; + targ.Add(btn); + Add(btn); + return btn; + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs b/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs new file mode 100644 index 0000000..e389714 --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/UIObjectHolder.cs @@ -0,0 +1,527 @@ +/* +This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +If a copy of the MPL was not distributed with this file, You can obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FSO.LotView; +using FSO.SimAntics; +using FSO.Common.Rendering.Framework.Model; +using Microsoft.Xna.Framework; +using FSO.LotView.Components; +using FSO.SimAntics.Entities; +using FSO.LotView.Model; +using FSO.Client.UI.Model; +using FSO.HIT; +using FSO.SimAntics.Model; +using Microsoft.Xna.Framework.Input; +using FSO.Client.UI.Framework; +using FSO.SimAntics.NetPlay.Model.Commands; +using FSO.Common; +using FSO.Client.UI.Controls; +using FSO.Common.Rendering.Framework; +using FSO.Content; +using FSO.Client; + +namespace Simitone.Client.UI.Panels +{ + public class UIObjectHolder //controls the object holder interface + { + public VM vm; + public World World; + public UILotControl ParentControl; + + public Direction Rotation; + public int MouseDownX; + public int MouseDownY; + private bool MouseIsDown; + private bool MouseWasDown; + private bool MouseClicked; + + private int OldMX; + private int OldMY; + private UpdateState LastState; //state for access from Sellback and friends. + public bool DirChanged; + public bool ShowTooltip; + public bool Roommate = true; + public bool UseNet = false; + + public event HolderEventHandler OnPickup; + public event HolderEventHandler OnDelete; + public event HolderEventHandler OnPutDown; + + public UIObjectSelection Holding; + + public UIObjectHolder(VM vm, World World, UILotControl parent) + { + this.vm = vm; + this.World = World; + ParentControl = parent; + } + + public void SetSelected(VMMultitileGroup Group) + { + if (Holding != null) ClearSelected(); + Holding = new UIObjectSelection(); + Holding.Group = Group; + if (!UseNet) Holding.Group.ExecuteEntryPoint(12, vm.Context); //User Pickup + Holding.PreviousTile = Holding.Group.BaseObject.Position; + Holding.Dir = Group.Objects[0].Direction; + Holding.OriginalDir = Holding.Dir; + VMEntity[] CursorTiles = new VMEntity[Group.Objects.Count]; + for (int i = 0; i < Group.Objects.Count; i++) + { + var target = Group.Objects[i]; + target.SetRoom(65534); + if (target is VMGameObject) ((ObjectComponent)target.WorldUI).ForceDynamic = true; + CursorTiles[i] = vm.Context.CreateObjectInstance(0x00000437, new LotTilePos(target.Position), FSO.LotView.Model.Direction.NORTH, true).Objects[0]; + CursorTiles[i].SetPosition(target.Position, Direction.NORTH, vm.Context); + ((ObjectComponent)CursorTiles[i].WorldUI).ForceDynamic = true; + } + Holding.TilePosOffset = new Vector2(0, 0); + Holding.CursorTiles = CursorTiles; + + uint guid; + var bobj = Group.BaseObject; + guid = bobj.Object.OBJ.GUID; + if (bobj.MasterDefinition != null) guid = bobj.MasterDefinition.GUID; + var catalogItem = Content.Get().WorldCatalog.GetItemByGUID(guid); + if (catalogItem != null) + { + var price = (int)catalogItem.Value.Price; + var dcPercent = VMBuildableAreaInfo.GetDiscountFor(catalogItem.Value, vm); + var finalPrice = (price * (100 - dcPercent)) / 100; + Holding.Price = finalPrice; + } + } + + public void MoveSelected(Vector2 pos, sbyte level) + { + Holding.TilePos = pos; + Holding.Level = level; + + //first, eject the object from any slots + for (int i = 0; i < Holding.Group.Objects.Count; i++) + { + var obj = Holding.Group.Objects[i]; + if (obj.Container != null) + { + obj.Container.ClearSlot(obj.ContainerSlot); + } + } + + //rotate through to try all configurations + var dir = Holding.Dir; + VMPlacementError status = VMPlacementError.Success; + if (!Holding.IsBought && !vm.TSOState.CanPlaceNewUserObject(vm)) status = VMPlacementError.TooManyObjectsOnTheLot; + else + { + for (int i = 0; i < 4; i++) + { + status = Holding.Group.ChangePosition(LotTilePos.FromBigTile((short)pos.X, (short)pos.Y, World.State.Level), dir, vm.Context, VMPlaceRequestFlags.UserPlacement).Status; + if (status != VMPlacementError.MustBeAgainstWall) break; + dir = (Direction)((((int)dir << 6) & 255) | ((int)dir >> 2)); + } + if (Holding.Dir != dir) Holding.Dir = dir; + } + + if (status != VMPlacementError.Success) + { + Holding.Group.ChangePosition(LotTilePos.OUT_OF_WORLD, Holding.Dir, vm.Context, VMPlaceRequestFlags.UserPlacement); + + Holding.Group.SetVisualPosition(new Vector3(pos, + (((Holding.Group.Objects[0].GetValue(VMStackObjectVariable.AllowedHeightFlags) & 1) == 1) ? 0 : 4f / 5f) + (World.State.Level - 1) * 2.95f), + //^ if we can't be placed on the floor, default to table height. + Holding.Dir, vm.Context); + } + + for (int i = 0; i < Holding.Group.Objects.Count; i++) + { + var target = Holding.Group.Objects[i]; + var tpos = target.VisualPosition; + tpos.Z = (World.State.Level - 1) * 2.95f; + Holding.CursorTiles[i].MultitileGroup.SetVisualPosition(tpos, Holding.Dir, vm.Context); + } + Holding.CanPlace = status; + } + + public void ClearSelected() + { + //TODO: selected items are only spooky ghosts of the items themselves. + // ...so that they dont cause serverside desyncs + // and so that clearing selections doesnt delete already placed objects. + if (Holding != null) + { + //try to copy back visible flags from holding copy + if (Holding.RealEnt != null && UseNet) + { + RecursiveUnhide(Holding.Group.BaseObject, Holding.RealEnt); + } + + if (UseNet || !Holding.IsBought) + { + Holding.Group.Delete(vm.Context); + } else + { + if (Holding.RealEnt == null) Holding.RealEnt = Holding.Group.BaseObject; + Holding.RealEnt.SetPosition(Holding.PreviousTile, Holding.Dir, vm.Context); + Holding.RealEnt.MultitileGroup.ExecuteEntryPoint(11, vm.Context); //User Placement + } + + for (int i = 0; i < Holding.CursorTiles.Length; i++) + { + Holding.CursorTiles[i].Delete(true, vm.Context); + ((ObjectComponent)Holding.CursorTiles[i].WorldUI).ForceDynamic = false; + } + } + Holding = null; + vm.Tick(); + } + + public void MouseDown(UpdateState state) + { + MouseIsDown = true; + MouseDownX = state.MouseState.X; + MouseDownY = state.MouseState.Y; + if (Holding != null) + { + Rotation = Holding.Dir; + DirChanged = false; + } + } + + public void MouseUp(UpdateState state) + { + MouseIsDown = false; + if (Holding != null && Holding.Clicked) + { + if (Holding.CanPlace == VMPlacementError.Success) + { + HITVM.Get().PlaySoundEvent((Holding.IsBought) ? UISounds.ObjectMovePlace : UISounds.ObjectPlace); + //ExecuteEntryPoint(11); //User Placement + var putDown = Holding; + var pos = Holding.Group.BaseObject.Position; + if (Holding.IsBought) + { + if (UseNet) + { + vm.SendCommand(new VMNetMoveObjectCmd + { + ObjectID = Holding.MoveTarget, + dir = Holding.Dir, + level = pos.Level, + x = pos.x, + y = pos.y + }); + } + else + { + //otherwise we're kind of already here? + Holding.PreviousTile = new LotTilePos(pos.x, pos.y, pos.Level); + } + } + else if (Holding.InventoryPID > 0) + { + vm.SendCommand(new VMNetPlaceInventoryCmd + { + ObjectPID = Holding.InventoryPID, + dir = Holding.Dir, + level = pos.Level, + x = pos.x, + y = pos.y + }); + } + else + { + var GUID = (Holding.Group.MultiTile) ? Holding.Group.BaseObject.MasterDefinition.GUID : Holding.Group.BaseObject.Object.OBJ.GUID; + if (UseNet || ParentControl.ActiveEntity != null) + { + vm.SendCommand(new VMNetBuyObjectCmd + { + GUID = GUID, + dir = Holding.Dir, + level = pos.Level, + x = pos.x, + y = pos.y + }); + } else + { + Holding.MoveTarget = Holding.Group.BaseObject.ObjectID; + Holding.PreviousTile = new LotTilePos(pos.x, pos.y, pos.Level); + } + } + ClearSelected(); + if (OnPutDown != null) OnPutDown(putDown, state); //call this after so that buy mode etc can produce more. + } + else + { + + } + } + + state.UIState.TooltipProperties.Show = false; + state.UIState.TooltipProperties.Opacity = 0; + ShowTooltip = false; + } + + private void ExecuteEntryPoint(int num) + { + for (int i = 0; i < Holding.Group.Objects.Count; i++) Holding.Group.Objects[i].ExecuteEntryPoint(num, vm.Context, true); + } + + public void SellBack(UIElement button) + { + if (Holding == null || !Roommate) return; + if (Holding.IsBought) + { + if (Holding.CanDelete) + { + vm.SendCommand(new VMNetDeleteObjectCmd + { + ObjectID = Holding.MoveTarget, + CleanupAll = true + }); + HITVM.Get().PlaySoundEvent(UISounds.MoneyBack); + } + else + { + ShowErrorAtMouse(LastState, VMPlacementError.CannotDeleteObject); + return; + } + } + OnDelete?.Invoke(Holding, null); //TODO: cleanup callbacks which don't need updatestate into another delegate. + ClearSelected(); + } + + public void Cancel() + { + Holding.Dir = Holding.OriginalDir; + OnDelete?.Invoke(Holding, null); + ClearSelected(); + } + + public void Update(UpdateState state, bool scrolled) + { + UseNet = true; + LastState = state; + if (ShowTooltip) state.UIState.TooltipProperties.UpdateDead = false; + MouseClicked = (MouseIsDown && (!MouseWasDown)); + + CursorType cur = CursorType.SimsMove; + if (Holding != null) + { + if (Roommate) cur = CursorType.SimsPlace; + if (state.KeyboardState.IsKeyDown(Keys.Delete)) + { + SellBack(null); + } + else if (state.KeyboardState.IsKeyDown(Keys.Escape)) + { + Cancel(); + } + } + if (Holding != null && Roommate) + { + if (MouseClicked) Holding.Clicked = true; + var mpos = FSOEnvironment.SoftwareKeyboard ? new Point(UIScreen.Current.ScreenWidth / 2, UIScreen.Current.ScreenHeight / 2) : state.MouseState.Position; + if (MouseIsDown && Holding.Clicked) + { + bool updatePos = MouseClicked; + int xDiff = mpos.X - MouseDownX; + int yDiff = mpos.Y - MouseDownY; + cur = CursorType.SimsRotate; + if (Math.Sqrt(xDiff * xDiff + yDiff * yDiff) > 64 && !FSOEnvironment.SoftwareKeyboard) + { + var from = World.EstTileAtPosWithScroll(new Vector2(MouseDownX, MouseDownY)); + var target = World.EstTileAtPosWithScroll(mpos.ToVector2()); + + var vec = target - from; + var dir = Math.Atan2(vec.Y, vec.X); + dir += Math.PI / 2; + if (dir < 0) dir += Math.PI * 2; + var newDir = (Direction)(1 << (((int)Math.Round(dir / (Math.PI / 2)) % 4) * 2)); + + if (newDir != Holding.Dir || MouseClicked) + { + updatePos = true; + HITVM.Get().PlaySoundEvent(UISounds.ObjectRotate); + Holding.Dir = newDir; + DirChanged = true; + } + } + if (updatePos) + { + MoveSelected(Holding.TilePos, Holding.Level); + if (!Holding.IsBought && Holding.CanPlace == VMPlacementError.Success && + ParentControl.ActiveEntity != null && ParentControl.ActiveEntity.TSOState.Budget.Value < Holding.Price) + Holding.CanPlace = VMPlacementError.InsufficientFunds; + if (Holding.CanPlace != VMPlacementError.Success) + { + state.UIState.TooltipProperties.Show = true; + state.UIState.TooltipProperties.Color = Color.Black; + state.UIState.TooltipProperties.Opacity = 1; + state.UIState.TooltipProperties.Position = new Vector2(MouseDownX, + MouseDownY); + state.UIState.Tooltip = GameFacade.Strings.GetString("137", ((int)Holding.CanPlace).ToString()); //"kPErr" + Holding.CanPlace.ToString() + //+ ((Holding.CanPlace == VMPlacementError.CannotPlaceComputerOnEndTable) ? "," : "")); + // comma added to curcumvent problem with language file. We should probably just index these with numbers? + state.UIState.TooltipProperties.UpdateDead = false; + ShowTooltip = true; + HITVM.Get().PlaySoundEvent(UISounds.Error); + } + else + { + state.UIState.TooltipProperties.Show = false; + state.UIState.TooltipProperties.Opacity = 0; + ShowTooltip = false; + } + } + } + else + { + var tilePos = World.EstTileAtPosWithScroll(new Vector2(mpos.X, mpos.Y) / FSOEnvironment.DPIScaleFactor) + Holding.TilePosOffset; + MoveSelected(tilePos, 1); + } + } + else if (MouseClicked) + { + //not holding an object, but one can be selected + var newHover = World.GetObjectIDAtScreenPos(state.MouseState.X / FSOEnvironment.DPIScaleFactor, state.MouseState.Y / FSOEnvironment.DPIScaleFactor, GameFacade.GraphicsDevice); + if (MouseClicked && (newHover != 0) && (vm.GetObjectById(newHover) is VMGameObject)) + { + var objGroup = vm.GetObjectById(newHover).MultitileGroup; + var objBasePos = objGroup.BaseObject.Position; + var success = (Roommate || objGroup.SalePrice > -1) ? objGroup.BaseObject.IsUserMovable(vm.Context, false) : VMPlacementError.ObjectNotOwnedByYou; + if (GameFacade.EnableMod) success = VMPlacementError.Success; + if (objBasePos.Level != World.State.Level) success = VMPlacementError.CantEffectFirstLevelFromSecondLevel; + if (success == VMPlacementError.Success) + { + var ghostGroup = (UseNet) ? vm.Context.GhostCopyGroup(objGroup) : objGroup ; + ghostGroup.ChangePosition(objGroup.BaseObject.Position, objGroup.BaseObject.Direction, vm.Context, VMPlaceRequestFlags.Default); + var canDelete = GameFacade.EnableMod || (objGroup.BaseObject.IsUserMovable(vm.Context, true)) == VMPlacementError.Success; + SetSelected(ghostGroup); + + Holding.RealEnt = objGroup.BaseObject; + if (UseNet) RecursiveHide(Holding.RealEnt); + Holding.CanDelete = canDelete; + Holding.MoveTarget = newHover; + var estBase = state.MouseState.Position; //FSOEnvironment.SoftwareKeyboard ? new Point(UIScreen.Current.ScreenWidth / 2, UIScreen.Current.ScreenHeight / 2) : state.MouseState.Position; + Holding.TilePosOffset = new Vector2(objBasePos.x / 16f, objBasePos.y / 16f) - World.EstTileAtPosWithScroll(new Vector2(estBase.X, estBase.Y) / FSOEnvironment.DPIScaleFactor); + if (OnPickup != null) OnPickup(Holding, state); + //ExecuteEntryPoint(12); //User Pickup + if (FSOEnvironment.SoftwareKeyboard) MouseIsDown = false; + } + else + { + ShowErrorAtMouse(state, success); + } + } + } + + if (ParentControl.MouseIsOn && !ParentControl.RMBScroll) + { + GameFacade.Cursor.SetCursor(cur); + } + + MouseWasDown = MouseIsDown; + } + + private void RecursiveHide(VMEntity ent) + { + ent.MultitileGroup.Objects.ForEach((x) => { + x.SetValue(VMStackObjectVariable.Hidden, 1); + + var slots = x.TotalSlots(); + for (int i=0; i 0) + { + var dir = (int)Holding.Dir << notches; + if (dir > 255) dir >>= 8; + newDir = (Direction)dir; + } else + { + var dir = ((int)Holding.Dir << 8) >> (-notches); + if (dir > 255) dir >>= 8; + newDir = (Direction)dir; + } + HITVM.Get().PlaySoundEvent(UISounds.ObjectRotate); + Holding.Dir = newDir; + DirChanged = true; + } + + public delegate void HolderEventHandler(UIObjectSelection holding, UpdateState state); + } + + public class UIObjectSelection + { + public short MoveTarget = 0; + + public VMMultitileGroup Group; + public VMEntity[] CursorTiles; + public LotTilePos PreviousTile; + public Direction OriginalDir = Direction.NORTH; + public Direction Dir = Direction.NORTH; + public Vector2 TilePos; + public Vector2 TilePosOffset; + public bool Clicked; + public VMPlacementError CanPlace; + public sbyte Level; + public int Price; + public uint InventoryPID = 0; + public bool CanDelete; + public VMEntity RealEnt; + + public bool IsBought + { + get + { + return (MoveTarget != 0); + } + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UIPickupPanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/UIPickupPanel.cs new file mode 100644 index 0000000..085c0ca --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/UIPickupPanel.cs @@ -0,0 +1,186 @@ +using FSO.Client; +using FSO.Client.UI.Controls; +using FSO.Client.UI.Framework; +using FSO.Common; +using FSO.Content; +using FSO.Files.Formats.IFF.Chunks; +using FSO.LotView.Components; +using FSO.SimAntics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Simitone.Client.UI.Controls; +using Simitone.Client.UI.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FSO.Common.Rendering.Framework.Model; +using FSO.Common.Rendering.Framework.IO; + +namespace Simitone.Client.UI.Panels +{ + public class UIPickupPanel : UIContainer + { + public UILabel TitleLabel; + public UILabel SubtextLabel; + public Texture2D Thumbnail; + public UI3DThumb Thumb3D; + protected UIMouseEventRef ClickHandler; + + public UICatButton CancelButton; + + public event Action OnResponse; + + public UIPickupPanel() + { + TitleLabel = new UILabel(); + TitleLabel.Position = new Vector2(450, 14); + TitleLabel.CaptionStyle = TitleLabel.CaptionStyle.Clone(); + TitleLabel.CaptionStyle.Size = 19; + TitleLabel.CaptionStyle.Color = UIStyle.Current.SecondaryText; + Add(TitleLabel); + + SubtextLabel = new UILabel(); + SubtextLabel.Position = new Vector2(450, 44); + SubtextLabel.CaptionStyle = SubtextLabel.CaptionStyle.Clone(); + SubtextLabel.CaptionStyle.Size = 12; + SubtextLabel.CaptionStyle.Color = UIStyle.Current.Text; + Add(SubtextLabel); + + CancelButton = new UICatButton(Content.Get().CustomUI.Get("cat_cancel.png").Get(GameFacade.GraphicsDevice)); + CancelButton.Position = new Vector2(174, 31); + CancelButton.OnButtonClick += CancelButton_OnButtonClick; + Add(CancelButton); + + ClickHandler = + ListenForMouse(new Rectangle(0, 0, 400, 128), new UIMouseEvent(OnMouseEvent)); + } + + private void OnMouseEvent(UIMouseEventType type, UpdateState state) + { + if (type == UIMouseEventType.MouseDown) + { + OnResponse?.Invoke(true); + } + } + + public override void Update(UpdateState state) + { + base.Update(state); + if (Visible) + { + SubtextLabel.CaptionStyle.Color = UIStyle.Current.Text * Opacity; + TitleLabel.CaptionStyle.Color = UIStyle.Current.SecondaryText * Opacity; + CancelButton.Opacity = Opacity; + } + } + + private void CancelButton_OnButtonClick(UIElement button) + { + OnResponse?.Invoke(false); + } + + public void SetInfo(VM vm, VMEntity entity) + { + var obj = entity.Object; + var def = entity.MasterDefinition; + if (def == null) def = entity.Object.OBJ; + + CTSS catString = obj.Resource.Get(def.CatalogStringsID); + if (catString != null) + { + TitleLabel.Caption = catString.GetString(0); + } + else + { + TitleLabel.Caption = entity.ToString(); + } + var World = vm.Context.World; + var sellback = entity.MultitileGroup.Price; + + var canDelete = entity.IsUserMovable(vm.Context, true) == FSO.SimAntics.Model.VMPlacementError.Success; + if (!canDelete) + { + SubtextLabel.Caption = GameFacade.Strings.GetString("136", "2").Replace("%", TitleLabel.Caption); //cannot be deleted + } else + { + if (sellback > 0) + { + SubtextLabel.Caption = GameFacade.Strings.GetString("136", "1").Replace("%", TitleLabel.Caption).Replace("$", "§" + sellback.ToString()); + } else + { + SubtextLabel.Caption = GameFacade.Strings.GetString("136", "0").Replace("%", TitleLabel.Caption); + } + + } + + if (entity is VMGameObject) + { + var objects = entity.MultitileGroup.Objects; + ObjectComponent[] objComps = new ObjectComponent[objects.Count]; + for (int i = 0; i < objects.Count; i++) + { + objComps[i] = (ObjectComponent)objects[i].WorldUI; + } + if (Thumbnail != null) Thumbnail.Dispose(); + if (Thumb3D != null) Thumb3D.Dispose(); + Thumbnail = null; Thumb3D = null; + if (FSOEnvironment.Enable3D) + { + Thumb3D = new UI3DThumb(entity); + } + else + { + var thumb = World.GetObjectThumb(objComps, entity.MultitileGroup.GetBasePositions(), GameFacade.GraphicsDevice); + Thumbnail = thumb; + } + } + else + { + if (Thumbnail != null) Thumbnail.Dispose(); + if (Thumb3D != null) Thumb3D.Dispose(); + Thumbnail = null; Thumb3D = null; + Thumbnail = null; + } + } + + public override void PreDraw(UISpriteBatch batch) + { + if (!Visible) return; + base.PreDraw(batch); + Thumb3D?.Draw(); + } + + public override void Draw(UISpriteBatch batch) + { + if (!Visible) return; + base.Draw(batch); + var targSize = 180f; + + float scale = 1f; + Texture2D thumb = null; + if (Thumb3D != null) + { + thumb = Thumb3D.Tex; + } + else if (Thumbnail != null) + { + scale = targSize / (float)Math.Sqrt(Thumbnail.Width * Thumbnail.Width + Thumbnail.Height * Thumbnail.Height); + thumb = Thumbnail; + } + + if (thumb != null) + { + var pos = new Vector2(thumb.Width * scale - 350 * 2, thumb.Height * scale - 128) / -2; + DrawLocalTexture(batch, thumb, null, pos, new Vector2(scale)); + } + } + + public override void Removed() + { + if (Thumbnail != null) Thumbnail.Dispose(); + if (Thumb3D != null) Thumb3D.Dispose(); + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Panels/UIQueryPanel.cs b/Client/Simitone/Simitone.Client/UI/Panels/UIQueryPanel.cs new file mode 100644 index 0000000..38fda69 --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Panels/UIQueryPanel.cs @@ -0,0 +1,317 @@ +using FSO.Client; +using FSO.Client.UI.Controls; +using FSO.Client.UI.Framework; +using FSO.Common.Utils; +using FSO.Content; +using FSO.Files.Formats.IFF.Chunks; +using FSO.LotView; +using FSO.LotView.Components; +using FSO.SimAntics; +using FSO.SimAntics.Model.TSOPlatform; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Simitone.Client.UI.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FSO.Common.Rendering.Framework.Model; +using FSO.Common; + +namespace Simitone.Client.UI.Panels +{ + public class UIQueryPanel : UICachedContainer + { + public UILabel Title; + public UILabel Body; + public UILabel TitleAd; + public UILabel Ad; + public Texture2D Thumbnail; + public bool Disposable; + public UI3DThumb Thumb3D; + public VMEntity ActiveEntity; + public Texture2D TitleTex; + public Texture2D ThumbTex; + public int Mode; + + public bool Shown; + private float _ShowPCT = 0f; + public float ShowPCT + { + get + { + return _ShowPCT; + } + set + { + _ShowPCT = value; + Size = new Vector2(Size.X, value * FullHeight + (1 - value) * 45); + Y = -(5 + Size.Y); + TitleAd.CaptionStyle.Color = UIStyle.Current.SecondaryText * (1-value); + } + } + + private bool _Active; + public bool Active + { + get + { + return _Active; + } + set + { + if (_Active != value) + { + GameFacade.Screens.Tween.To(this, 0.5f, new Dictionary() { { "Opacity", (value)?1f:0f } }, TweenQuad.EaseOut); + } + _Active = value; + } + } + + public float FullHeight = 45; + private string[] AdStrings; + private World World; + + public UIQueryPanel(World world) + { + World = world; + + AdStrings = new string[14]; + for (int i = 0; i < 14; i++) + { + string str = GameFacade.Strings.GetString("160", (i).ToString()); + AdStrings[i] = ((i < 7) ? str.Substring(0, str.Length - 2) + "{0}" : str) + "\r\n"; + } + + Title = new UILabel(); + Title.Size = new Vector2(546 - 50, 0); + Title.Position = new Vector2(25 + 210, 8); + Title.CaptionStyle = Title.CaptionStyle.Clone(); + Title.CaptionStyle.Size = 19; + Title.CaptionStyle.Color = UIStyle.Current.Text; + Add(Title); + + TitleAd = new UILabel(); + TitleAd.Size = new Vector2(546-50, 0); + TitleAd.Position = new Vector2(25 + 210, 8); + TitleAd.Alignment = TextAlignment.Top | TextAlignment.Right; + TitleAd.CaptionStyle = TitleAd.CaptionStyle.Clone(); + TitleAd.CaptionStyle.Size = 12; + TitleAd.CaptionStyle.Color = UIStyle.Current.SecondaryText; + Add(TitleAd); + + Body = new UILabel(); + Body.Wrapped = true; + Body.Size = new Vector2(518, 0); + Body.Position = new Vector2(14 + 210, 55); + Body.CaptionStyle = Body.CaptionStyle.Clone(); + Body.CaptionStyle.Size = 15; + Body.CaptionStyle.Color = UIStyle.Current.Text; + Add(Body); + + Ad = new UILabel(); + Ad.Wrapped = true; + Ad.Alignment = TextAlignment.Top | TextAlignment.Right; + Ad.Size = new Vector2(518, 0); + Ad.Position = new Vector2(14+210, 55); //placed below body + Ad.CaptionStyle = Ad.CaptionStyle.Clone(); + Ad.CaptionStyle.Size = 12; + Ad.CaptionStyle.Color = UIStyle.Current.SecondaryText; + Add(Ad); + Size = new Vector2(210 + 546, 45); + + ShowPCT = ShowPCT; + Opacity = 0; + Active = Active; + + ThumbTex = Content.Get().CustomUI.Get("cat_thumb_bg.png").Get(GameFacade.GraphicsDevice); + TitleTex = Content.Get().CustomUI.Get("query_title.png").Get(GameFacade.GraphicsDevice); + + Title.Alignment = TextAlignment.Left | TextAlignment.Top; + Body.Alignment = TextAlignment.Left | TextAlignment.Top; + } + + public override void Update(UpdateState state) + { + Visible = Opacity > 0; + if (Thumb3D != null && Visible) Invalidate(); + base.Update(state); + } + + public void SetShown(bool show) + { + if (Shown && show == Shown && FullHeight != Size.Y) + { + ShowPCT = Size.Y / FullHeight; + } + if (show) + { + GameFacade.Screens.Tween.To(this, 0.5f, new Dictionary() { { "ShowPCT", 1f } }, TweenQuad.EaseOut); + } else + { + GameFacade.Screens.Tween.To(this, 0.5f, new Dictionary() { { "ShowPCT", 0f } }, TweenQuad.EaseOut); + } + + Shown = show; + } + + public void SetInfo(VM vm, VMEntity entity, bool bought) + { + ActiveEntity = entity; + var obj = entity.Object; + var def = entity.MasterDefinition; + if (def == null) def = entity.Object.OBJ; + + var item = Content.Get().WorldCatalog.GetItemByGUID(def.GUID); + + CTSS catString = obj.Resource.Get(def.CatalogStringsID); + if (catString != null) + { + Body.Caption = catString.GetString(1); + Title.Caption = catString.GetString(0); + } + else + { + Body.Caption = "No information available."; + Title.Caption = entity.ToString(); + } + + StringBuilder motivesString = new StringBuilder(); + if (def.RatingHunger != 0) { motivesString.AppendFormat(AdStrings[0], def.RatingHunger); } + if (def.RatingComfort != 0) { motivesString.AppendFormat(AdStrings[1], def.RatingComfort); } + if (def.RatingHygiene != 0) { motivesString.AppendFormat(AdStrings[2], def.RatingHygiene); } + if (def.RatingBladder != 0) { motivesString.AppendFormat(AdStrings[3], def.RatingBladder); } + if (def.RatingEnergy != 0) { motivesString.AppendFormat(AdStrings[4], def.RatingEnergy); } + if (def.RatingFun != 0) { motivesString.AppendFormat(AdStrings[5], def.RatingFun); } + if (def.RatingRoom != 0) { motivesString.AppendFormat(AdStrings[6], def.RatingRoom); } + + var sFlags = def.RatingSkillFlags; + for (int i = 0; i < 7; i++) + { + if ((sFlags & (1 << i)) > 0) motivesString.Append(AdStrings[i + 7]); + } + + var strings = motivesString.ToString().Replace("\r", "").Split('\n').TakeWhile(x => x != ""); + TitleAd.Caption = string.Join("\n", strings.Take(2)); + Ad.Caption = string.Join(", ", strings); + + SetTitleSize(); + + if (entity is VMGameObject) + { + var objects = entity.MultitileGroup.Objects; + ObjectComponent[] objComps = new ObjectComponent[objects.Count]; + for (int i = 0; i < objects.Count; i++) + { + objComps[i] = (ObjectComponent)objects[i].WorldUI; + } + if (Thumbnail != null && Disposable) Thumbnail.Dispose(); + if (Thumb3D != null) Thumb3D.Dispose(); + Thumbnail = null; Thumb3D = null; + if (FSOEnvironment.Enable3D) + { + Thumb3D = new UI3DThumb(entity); + } + else + { + var thumb = World.GetObjectThumb(objComps, entity.MultitileGroup.GetBasePositions(), GameFacade.GraphicsDevice); + Thumbnail = thumb; + } + } + else + { + if (Thumbnail != null && Disposable) Thumbnail.Dispose(); + if (Thumb3D != null) Thumb3D.Dispose(); + Thumbnail = null; Thumb3D = null; + Thumbnail = null; + } + + FullHeight = Body.CaptionStyle.LineHeight * Body.NumLines + 45 + 20; + FullHeight = Math.Max(200, FullHeight); + if (Ad.Caption != "") + { + Ad.Y = FullHeight; + FullHeight += 25; + } + Disposable = true; + } + + public override void Removed() + { + if (Thumbnail != null && Disposable) Thumbnail.Dispose(); + if (Thumb3D != null) Thumb3D.Dispose(); + } + + public void SetInfo(Texture2D thumb, string name, string description, int price, bool doDispose) + { + ActiveEntity = null; + Body.Caption = description; + Title.Caption = name; + + SetTitleSize(); + + FullHeight = Body.CaptionStyle.LineHeight * Body.NumLines + 45 + 20; + FullHeight = Math.Max(200, FullHeight); + + TitleAd.Caption = ""; + Ad.Caption = ""; + + /*StringBuilder motivesString = new StringBuilder(); + motivesString.AppendFormat(GameFacade.Strings.GetString("206", "19") + "${0}\r\n", price); + MotivesText.CurrentText = motivesString.ToString();*/ + + if (Thumbnail != null && Disposable) Thumbnail.Dispose(); + if (Thumb3D != null) Thumb3D.Dispose(); + Thumbnail = null; Thumb3D = null; + Thumbnail = thumb; + //UpdateImagePosition(); + Disposable = doDispose; + } + + private void SetTitleSize() + { + Title.CaptionStyle.Size = 19; + var calc = Title.CaptionStyle.MeasureString(Title.Caption); + if (calc.X > 400) + { + Title.CaptionStyle.Size = 15; + Title.Y = 12; + } else + { + Title.Y = 8; + } + } + + public override void InternalDraw(UISpriteBatch batch) + { + base.InternalDraw(batch); + DrawLocalTexture(batch, TitleTex, new Rectangle(0, 0, 45, 45), new Vector2(210, 0), Vector2.One, UIStyle.Current.Bg); + DrawLocalTexture(batch, TextureGenerator.GetPxWhite(batch.GraphicsDevice), null, new Vector2(255, 0), new Vector2(Size.X-(210+90), 45), UIStyle.Current.Bg); + DrawLocalTexture(batch, TitleTex, new Rectangle(45, 0, 45, 45), new Vector2(Size.X - 45, 0), Vector2.One, UIStyle.Current.Bg); + + DrawLocalTexture(batch, TextureGenerator.GetPxWhite(batch.GraphicsDevice), null, new Vector2(210, 45), new Vector2(Size.X, Size.Y-45), UIStyle.Current.TitleBg); + DrawLocalTexture(batch, ThumbTex, null, new Vector2(0, FullHeight - 200), Vector2.One, UIStyle.Current.TitleBg * ((255-TitleAd.CaptionStyle.Color.A)/255f)); + + var targSize = 180f; + + float scale = 1f; + Texture2D thumb = null; + if (Thumb3D != null) + { + Thumb3D.Draw(); + thumb = Thumb3D.Tex; + } + else if (Thumbnail != null) + { + scale = targSize / (float)Math.Sqrt(Thumbnail.Width * Thumbnail.Width + Thumbnail.Height * Thumbnail.Height); + thumb = Thumbnail; + } + + if (thumb != null) { + var pos = new Vector2(thumb.Width * scale - 200, thumb.Height * scale - (FullHeight - 100) * 2) / -2; + DrawLocalTexture(batch, thumb, null, pos, new Vector2(scale)); + } + } + } +} diff --git a/Client/Simitone/Simitone.Client/UI/Screens/TS1CASScreen.cs b/Client/Simitone/Simitone.Client/UI/Screens/TS1CASScreen.cs new file mode 100644 index 0000000..36f8fa8 --- /dev/null +++ b/Client/Simitone/Simitone.Client/UI/Screens/TS1CASScreen.cs @@ -0,0 +1,872 @@ +using FSO.Client; +using FSO.Client.UI.Framework; +using FSO.Common; +using FSO.Common.Utils; +using FSO.SimAntics; +using FSO.SimAntics.Engine.TSOTransaction; +using FSO.SimAntics.NetPlay; +using FSO.SimAntics.NetPlay.Drivers; +using FSO.SimAntics.NetPlay.Model; +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; +using FSO.Common.Rendering.Framework.Model; +using FSO.Common.Rendering.Framework.Camera; +using Microsoft.Xna.Framework; +using FSO.LotView.RC; +using FSO.LotView.Utils; +using FSO.LotView.Model; +using FSO.Content; +using FSO.Vitaboy; +using FSO.Content.TS1; +using Simitone.Client.UI.Panels.CAS; +using Simitone.Client.UI.Controls; +using FSO.SimAntics.Model; + +namespace Simitone.Client.UI.Screens +{ + public class TS1CASScreen : UIScreen + { + private FSO.LotView.World World; + public FSO.SimAntics.VM vm { get; set; } + public VMNetDriver Driver; + public BasicCamera Cam; + public VMAvatar[] HeadAvatars; + public VMAvatar[] BodyAvatars; + public List ActiveHeads; + public List ActiveBodies; + public List ActiveHeadTex; + public List ActiveHandgroupTex; + public List ActiveBodyTex; + private bool Dead; + + private float _FamilySimInterp; + public float FamilySimInterp + { + set + { + CameraInterp(value); + CASPanel.Position = new Vector2((ScreenWidth - 500) / 2, 10 - (282 * (1-value))); + FamilyPanel.ShowI = 1-Math.Abs(value); + + _FamilySimInterp = value; + } + get + { + return _FamilySimInterp; + } + } + + 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 string CurrentCode = "ma"; + public string CurrentSkin = "lgt"; + private bool CurrentChild; + + private UICASMode Mode = UICASMode.FamilyEdit; + + public List WIPFamily = new List(); + public List RepresentFamily = new List(); + + public UISimCASPanel CASPanel; + public UIFamilyCASPanel FamilyPanel; + public UITwoStateButton BackButton; + public UITwoStateButton AcceptButton; + + public Vector3[] ModePositions = new Vector3[] + { + new Vector3(157.3843f, 28.92333f, 23.25105f), + new Vector3(119.5611f, 6.122346f, 104.0364f), + new Vector3(114.0793f, 10f, 64.67827f) + }; + + public Vector3[] ModeTargets = new Vector3[] + { + new Vector3(150.3057f, 26.01005f, 29.6858f), + new Vector3(111.7678f, 3.936443f, 98.164f), + new Vector3(104.5736f, 6.896059f, 64.59684f) + }; + + public Vector3[] SinTransitions = new Vector3[] + { + new Vector3(12f, 0, 0), + new Vector3(-6f, 0, 0), + new Vector3() + }; + + public void CameraInterp(float value) + { + if (value < -1) return; + var prev = (int)(value + 1); + var next = (int)(Math.Ceiling(value) + 1); + if (next > 2) next = 2; + + var pos1 = ModePositions[prev]; + var pos2 = ModePositions[next]; + var targ1 = ModeTargets[prev] - pos1; + var targ2 = ModeTargets[next] - pos2; + + value = (float)DirectionUtils.PosMod(value, 1.0); + var camvec = Vector3.Lerp(targ1, targ2, value); + var campos = Vector3.Lerp(pos1, pos2, value); + + campos += (float)Math.Sin(value * Math.PI) * SinTransitions[prev]; + + Cam.Position = campos; + Cam.Target = campos + camvec; + } + + private void PopulateSimType(string simtype) + { + CurrentCode = simtype; + var heads = Content.Get().BCFGlobal.CollectionsByName["c"].ClothesByAvatarType[simtype]; + if (simtype[1] == 'c') simtype += "chd"; + var bodies = Content.Get().BCFGlobal.CollectionsByName["b"].ClothesByAvatarType[simtype]; + + //todo: search for textures + + var tex = (TS1AvatarTextureProvider)Content.Get().AvatarTextures; + var texnames = tex.GetAllNames(); + ActiveHeads = heads; + ActiveBodies = bodies; + + ActiveHeadTex = heads.Select(x => RemoveExt(texnames.FirstOrDefault(y => y.StartsWith(ExtractID(x, CurrentSkin))))).ToList(); + 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 282) + XLast = state.MouseState.X; + } + else + { + if (state.MouseState.Y / (float)UIScreen.Current.ScreenHeight > 0.625f) + { + BodySpeed = ((XLast - state.MouseState.X) / 200f) * frac; + moving = 1; + } + else + { + HeadSpeed = ((XLast - state.MouseState.X) / 200f) * frac; + moving = 2; + } + 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 = vm.Context.GetRoomAt(LotTilePos.FromBigTile(28, 21, 1)); + foreach (var body in BodyAvatars) body.SetRoom(room); + foreach (var head in HeadAvatars) head.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 -1) + { + var sim = WIPFamily[i]; + + //load this family member's traits into the editor + CASPanel.FirstNameTextBox.CurrentText = sim.Name; + CASPanel.BioEdit.CurrentText = sim.Bio; + switch (sim.Gender) + { + case 0: CurrentCode = "ma"; break; + case 1: CurrentCode = "fa"; break; + case 2: CurrentCode = "mc"; break; + case 3: CurrentCode = "fc"; break; + } + for (int j = 0; j < 5; j++) + { + CASPanel.Personalities[j].Points = sim.Personality[j]; + } + CurrentSkin = sim.SkinColor; + + PopulateSimType(CurrentCode); + + //find index for the sims body and head + + BodyPosition = ActiveBodies.IndexOf(sim.Body) - 8; + HeadPosition = ActiveHeads.IndexOf(sim.Head) - 8; + } else + { + CASPanel.FirstNameTextBox.CurrentText = ""; + CASPanel.BioEdit.CurrentText = ""; + CurrentCode = "ma"; + CurrentSkin = "lgt"; + for (int j = 0; j < 5; j++) + { + CASPanel.Personalities[j].Points = 0; + } + PopulateSimType(CurrentCode); + BodyPosition = - 8; + HeadPosition = - 8; + } + CASPanel.AType = CurrentCode; + CASPanel.SkinType = CurrentSkin; + CASPanel.UpdateTotalPoints(); + CASPanel.UpdateType(); + } + + private void Accept(UIElement button) + { + switch (Mode) + { + case UICASMode.SimEdit: + //add or replace the sim in the family + AcceptMember(); + break; + case UICASMode.FamilyEdit: + //add or replace the family in the neighbourhood + //need to generate an actual FAMI and save it for this + break; + case UICASMode.FamilySelect: + //accept button here is move in. notify the neighbourhood screen that we're moving in now. + break; + } + SetMode((UICASMode)(((int)Mode) - 1)); + } + + private void GoBack(UIElement button) + { + SetMode((UICASMode)(((int)Mode) - 1)); + } + + public void SetMode(UICASMode mode) + { + if (mode == UICASMode.ToNeighborhood) + { + //todo: animate into this + CleanupLastWorld(); + Dead = true; + GameController.EnterGameMode("", false); + } else if (mode == UICASMode.FamilyEdit) + { + FamilyPanel.Reset(); + } + GameFacade.Screens.Tween.To(this, 1f, new Dictionary { { "FamilySimInterp", (int)mode-1 } }, TweenQuad.EaseInOut); + Mode = mode; + } + + private void CASPanel_OnRandom() + { + var rand = new Random(); + HeadPosition = rand.Next(ActiveHeads.Count); + BodyPosition = rand.Next(ActiveBodies.Count); + PopulateReal(); + HeadPositionLast = 0; + BodyPositionLast = 0; + } + + private void CASPanel_OnCollectionChange() + { + if (CurrentSkin == CASPanel.SkinType && CurrentCode == CASPanel.AType) return; + CurrentSkin = CASPanel.SkinType; + if (vm == null) return; + PopulateSimType(CASPanel.AType); + } + + public override void Update(UpdateState state) + { + base.Update(state); + ModePositions[2].Y = 11; + ModeTargets[2].Y = 7.896059f; + if (Dead) return; + if (vm == null) InitializeLot(); + vm.Update(); + if (World != null && Cam == null) + { + var rcs = (WorldStateRC)(World.State); + Cam = (WorldCamera3D)rcs.Camera; + rcs.FixedCam = true; + rcs.CameraMode = true; + SetMode(UICASMode.FamilyEdit); + //FamilySimInterp = FamilySimInterp; + } + + if (World.State.Level != 2) + { + World.State.Level = 2; + World.State.DrawRoofs = true; + } + + vm.Context.Clock.Minutes = 0; + vm.Context.Clock.Hours = 12; + + if (state.NewKeys.Contains(Microsoft.Xna.Framework.Input.Keys.LeftControl)) + { + SetMode(UICASMode.SimEdit); + } + + var disableAccept = false; + switch (Mode) + { + case UICASMode.SimEdit: + if (CASPanel.FirstNameTextBox.CurrentText.Length == 0) disableAccept = true; + break; + } + + AcceptButton.Disabled = disableAccept; + AcceptButton.ForceState = disableAccept ? 0 : -1; + AcceptButton.Opacity = disableAccept ? 0.5f : 1; + + if (Mode == UICASMode.SimEdit) + UpdateCarousel(state); + + for (int i=0; i<18; i++) + { + var relPos = HeadPosition + i - 9; + relPos = (float)DirectionUtils.PosMod(relPos, 18); + if (relPos > 9) relPos += 6; + var angle = (relPos / 24) * (Math.PI * 2); + var pos = new Vector3(28.5f + 4.5f*(float)Math.Cos(angle), 21.5f+ 4.5f * (float)Math.Sin(angle), 0); + HeadAvatars[i].RadianDirection = (float)angle + (float)Math.PI/2; + HeadAvatars[i].VisualPosition = pos; + } + + 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(28.5f + 4.5f * (float)Math.Cos(angle), 21.5f + 4.5f * (float)Math.Sin(angle), 0); + BodyAvatars[i].RadianDirection = (float)angle + (float)Math.PI / 2; + BodyAvatars[i].VisualPosition = pos; + } + + foreach (var fam in RepresentFamily) + { + for (int i = 0; i < 16; i++) + { + fam.SetMotiveData((VMMotive)i, 100); + } + var q = new List(fam.Thread.Queue); + foreach (var action in q) + fam.Thread.CancelAction(action.UID); + } + } + + public void SetFamilyMember(int index) + { + if (RepresentFamily.Count <= index) + { + var member = (VMAvatar)vm.Context.CreateObjectInstance(0x7FD96B54, LotTilePos.OUT_OF_WORLD + + , Direction.EAST, false).BaseObject; + + RepresentFamily.Add(member); + } + + var fam = RepresentFamily[index]; + + fam.SetPosition(LotTilePos.FromBigTile(34, 31, 1) + + new LotTilePos((short)((((index + 1) / 2) % 2) * 8), (short)(((index % 2) * 2 - 1) * ((index + 1) / 2) * 10), 0), Direction.EAST, vm.Context, VMPlaceRequestFlags.AllowIntersection); + var data = WIPFamily[index]; + //set this person's body and head + + fam.Name = data.Name; + fam.Avatar.Skeleton = Content.Get().AvatarSkeletons.Get((data.Gender>1) ? "child.skel" : "adult.skel").Clone(); + fam.Avatar.BaseSkeleton = fam.Avatar.Skeleton.Clone(); + fam.Avatar.ReloadSkeleton(); + + fam.SetPersonData(VMPersonDataVariable.PersonsAge, (short)((data.Gender > 1) ? 12 : 21)); + fam.InitBodyData(vm.Context); + + var oft = new Outfit() { TS1AppearanceID = data.Body + ".apr", TS1TextureID = data.BodyTex }; + var code = (data.Gender > 1) ? "u" : ((data.Gender == 0) ? "m" : "f"); + var hg = data.HandgroupTex; + 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 } + } + } + }; + + fam.BodyOutfit = new FSO.SimAntics.Model.VMOutfitReference(oft); + fam.HeadOutfit = new FSO.SimAntics.Model.VMOutfitReference(new Outfit() { TS1AppearanceID = data.Head+".apr", TS1TextureID = data.HeadTex }); + } + + public void AcceptMember() + { + var mem = BuildMember(); + if (EditIndex == -1) + { + WIPFamily.Add(mem); + SetFamilyMember(WIPFamily.Count - 1); + } else + { + WIPFamily[EditIndex] = mem; + SetFamilyMember(EditIndex); + } + } + + public CASFamilyMember BuildMember() + { + //build the object out of the contents of various menus + var i = (int)DirectionUtils.PosMod(Math.Round(BodyPosition+8), ActiveBodies.Count); + var j = (int)DirectionUtils.PosMod(Math.Round(HeadPosition+8), ActiveHeads.Count); + var sim = new CASFamilyMember() + { + Name = CASPanel.FirstNameTextBox.CurrentText, + Bio = CASPanel.BioEdit.CurrentText, + Body = ActiveBodies[i], + BodyTex = ActiveBodyTex[i], + HandgroupTex = ActiveHandgroupTex[i], + Head = ActiveHeads[j], + HeadTex = ActiveHeadTex[j], + Gender = (short)(((CurrentCode[0] == 'm') ? 0 : 1) | ((CurrentCode[1] == 'c') ? 2 : 0)), + Personality = CASPanel.Personalities.Select(x => (short)x.Points).ToArray(), + SkinColor = CurrentSkin + }; + return sim; + } + + public override void GameResized() + { + base.GameResized(); + World?.GameResized(); + BackButton.Position = new Vector2(25, ScreenHeight - 140); + AcceptButton.Position = new Vector2(ScreenWidth - 140, ScreenHeight - 140); + FamilySimInterp = FamilySimInterp; + } + + public override void PreDraw(UISpriteBatch batch) + { + base.PreDraw(batch); + vm?.PreDraw(); + } + + public override void Draw(UISpriteBatch batch) + { + base.Draw(batch); + } + + public void CleanupLastWorld() + { + if (vm == null) return; + + //clear our cache too, if the setting lets us do that + TimedReferenceController.Clear(); + TimedReferenceController.Clear(); + VM.ClearAssembled(); + + vm.Context.Ambience.Kill(); + foreach (var ent in vm.Entities) + { //stop object sounds + var threads = ent.SoundThreads; + for (int i = 0; i < threads.Count; i++) + { + threads[i].Sound.RemoveOwner(ent.ObjectID); + } + threads.Clear(); + } + vm.CloseNet(VMCloseNetReason.LeaveLot); + GameFacade.Scenes.Remove(World); + World.Dispose(); + vm.SuppressBHAVChanges(); + vm = null; + World = null; + Driver = null; + } + + public void InitializeLot() + { + CleanupLastWorld(); + + if (FSOEnvironment.Enable3D) + { + var rc = new FSO.LotView.RC.WorldRC(GameFacade.GraphicsDevice); + World = rc; + } + else + { + World = new FSO.LotView.World(GameFacade.GraphicsDevice); + } + + World.Opacity = 1; + GameFacade.Scenes.Add(World); + + var globalLink = new VMTS1GlobalLinkStub(); + Driver = new VMServerDriver(globalLink); + + vm = new VM(new VMContext(World), Driver, new UIHeadlineRendererProvider()); + vm.ListenBHAVChanges(); + vm.Init(); + + using (var file = new BinaryReader(File.OpenRead(Path.Combine(FSOEnvironment.ContentDir, "cas.fsov")))) + { + var marshal = new FSO.SimAntics.Marshals.VMMarshal(); + marshal.Deserialize(file); + + vm.Load(marshal); + vm.Reset(); + } + vm.Tick(); + + vm.Context.Clock.Hours = 12; + vm.TSOState.Size = (10) | (3 << 8); + vm.Context.UpdateTSOBuildableArea(); + vm.MyUID = 1; + var settings = GlobalSettings.Default; + var myClient = new VMNetClient + { + PersistID = 1, + RemoteIP = "local", + AvatarState = new VMNetAvatarPersistState() + { + Name = settings.LastUser ?? "", + DefaultSuits = new VMAvatarDefaultSuits(settings.DebugGender), + BodyOutfit = settings.DebugBody, + HeadOutfit = settings.DebugHead, + PersistID = 1, + SkinTone = (byte)settings.DebugSkin, + Gender = (short)(settings.DebugGender ? 1 : 0), + Permissions = FSO.SimAntics.Model.TSOPlatform.VMTSOAvatarPermissions.Admin, + Budget = 1000000 + } + + }; + + var server = (VMServerDriver)Driver; + server.ConnectClient(myClient); + + HeadAvatars = new VMAvatar[18]; + for (int i=0; i<18; i++) + { + HeadAvatars[i] = (VMAvatar)vm.Context.CreateObjectInstance(0x7FD96B54, LotTilePos.OUT_OF_WORLD, Direction.NORTH, true).BaseObject; + } + + BodyAvatars = new VMAvatar[18]; + for (int i = 0; i < 18; i++) + { + BodyAvatars[i] = (VMAvatar)vm.Context.CreateObjectInstance(0x7FD96B54, LotTilePos.OUT_OF_WORLD, Direction.NORTH, true).BaseObject; + } + + PopulateSimType("ma"); + } + } + + public class CASFamilyMember + { + public string Name; + public string SkinColor; + public short Gender; //adult 0,1... child 2,3 + + public string Head; + public string HeadTex; + public string Body; + public string BodyTex; + public string HandgroupTex; + + public short[] Personality = new short[5]; + public string Bio; + public uint RefGUID; //for family editing. TODO. + public int ReplaceIndex; //for editing existing sims + } + + public enum UICASMode : int + { + ToNeighborhood = -1, + FamilySelect = 0, + FamilyEdit = 1, + SimEdit = 2, + } +}