diff --git a/Common/ac/dialogtopic.cpp b/Common/ac/dialogtopic.cpp index b86f851716..2421aa7168 100644 --- a/Common/ac/dialogtopic.cpp +++ b/Common/ac/dialogtopic.cpp @@ -29,7 +29,7 @@ void DialogTopic::ReadFromFile(Stream *in) topicFlags = in->ReadInt32(); } -void DialogTopic::ReadFromSavegame(Common::Stream *in) +void DialogTopic::ReadFromSavegame(Common::Stream *in, DialogSvgVersion /*svg_ver*/) { in->ReadArrayOfInt32(optionflags, MAXTOPICOPTIONS); } diff --git a/Common/ac/dialogtopic.h b/Common/ac/dialogtopic.h index c47886c0c1..b364662da4 100644 --- a/Common/ac/dialogtopic.h +++ b/Common/ac/dialogtopic.h @@ -29,6 +29,16 @@ using namespace AGS; // FIXME later #define DTFLG_SHOWPARSER 1 // show parser in this topic #define DCHAR_NARRATOR 999 #define DCHAR_PLAYER 998 + +// RoomStatus runtime save format +// TODO: move to the engine code +enum DialogSvgVersion +{ + kDialogSvgVersion_Initial = 0, + kDialogSvgVersion_40008 = 4000008, // custom properties +}; + + struct DialogTopic { char optionnames[MAXTOPICOPTIONS][150]; int optionflags[MAXTOPICOPTIONS]; @@ -40,7 +50,7 @@ struct DialogTopic { void ReadFromFile(Common::Stream *in); - void ReadFromSavegame(Common::Stream *in); + void ReadFromSavegame(Common::Stream *in, DialogSvgVersion svg_ver); void WriteToSavegame(Common::Stream *out) const; }; diff --git a/Common/ac/gamesetupstruct.cpp b/Common/ac/gamesetupstruct.cpp index e3aa478a30..fdf1379202 100644 --- a/Common/ac/gamesetupstruct.cpp +++ b/Common/ac/gamesetupstruct.cpp @@ -49,7 +49,10 @@ void GameSetupStruct::Free() audioClips.clear(); audioClipTypes.clear(); + audioclipProps.clear(); charProps.clear(); + guiProps.clear(); + dialogProps.clear(); viewNames.clear(); } @@ -206,11 +209,11 @@ HGameFileError GameSetupStruct::read_customprops(Common::Stream *in, GameDataVer errors += Properties::ReadValues(invProps[i], in); } - if (errors > 0) - return new MainGameFileError(kMGFErr_InvalidPropertyValues); + if (errors > 0) + return new MainGameFileError(kMGFErr_InvalidPropertyValues); - for (int i = 0; i < numviews; ++i) - viewNames[i] = String::FromStream(in); + for (int i = 0; i < numviews; ++i) + viewNames[i] = String::FromStream(in); for (int i = 0; i < numinvitems; ++i) invScriptNames[i] = String::FromStream(in); diff --git a/Common/ac/gamesetupstruct.h b/Common/ac/gamesetupstruct.h index 606a6cdd5a..69fd82be2b 100644 --- a/Common/ac/gamesetupstruct.h +++ b/Common/ac/gamesetupstruct.h @@ -62,6 +62,9 @@ struct GameSetupStruct : public GameSetupStructBase AGS::Common::PropertySchema propSchema; std::vector charProps; AGS::Common::StringIMap invProps[MAX_INV]; + std::vector audioclipProps; + std::vector dialogProps; + std::vector guiProps; // NOTE: although the view names are stored in game data, they are never // used, nor registered as script exports; numeric IDs are used to // reference views instead. diff --git a/Common/game/main_game_file.cpp b/Common/game/main_game_file.cpp index 3eaff0ee8d..568df9562d 100644 --- a/Common/game/main_game_file.cpp +++ b/Common/game/main_game_file.cpp @@ -545,6 +545,39 @@ HError GameDataExtReader::ReadBlock(Stream *in, int /*block_id*/, const String & // reserve few more 32-bit values (for a total of 10) _in->Seek(sizeof(int32_t) * 9); } + else if (ext_id.CompareNoCase("v400_customprops") == 0) + { + auto &game = _ents.Game; + int errors = 0; + game.audioclipProps.resize(game.audioClips.size()); + game.dialogProps.resize(game.numdialog); + game.guiProps.resize(game.numgui); + + size_t num_clips = in->ReadInt32(); + if (num_clips != game.audioClips.size()) + return new Error(String::FromFormat("Mismatching number of audio clips: read %zu expected %zu", num_clips, _ents.Game.audioClips.size())); + for (size_t i = 0; i < game.audioClips.size(); ++i) + { + errors += Properties::ReadValues(game.audioclipProps[i], in); + } + size_t num_dialogs = in->ReadInt32(); + if (num_dialogs != game.numdialog) + return new Error(String::FromFormat("Mismatching number of dialogs: read %zu expected %zu", num_dialogs, game.numdialog)); + for (size_t i = 0; i < game.numdialog; ++i) + { + errors += Properties::ReadValues(game.dialogProps[i], in); + } + size_t num_gui = in->ReadInt32(); + if (num_gui != game.numgui) + return new Error(String::FromFormat("Mismatching number of gui: read %zu expected %zu", num_gui, game.numgui)); + for (size_t i = 0; i < game.numgui; ++i) + { + errors += Properties::ReadValues(game.guiProps[i], in); + } + + if (errors > 0) + return new MainGameFileError(kMGFErr_InvalidPropertyValues); + } else { return new MainGameFileError(kMGFErr_ExtUnknown, String::FromFormat("Type: %s", ext_id.GetCStr())); diff --git a/Common/game/room_file.cpp b/Common/game/room_file.cpp index 13365e4e12..1e9239bc3e 100644 --- a/Common/game/room_file.cpp +++ b/Common/game/room_file.cpp @@ -355,6 +355,31 @@ HError ReadExt_400_WalkOpts(RoomStruct *room, Stream *in, RoomFileVersion data_v return HError::None(); } +HError ReadExt_400_CustomProps(RoomStruct *room, Stream *in, RoomFileVersion data_ver) +{ + size_t region_count = in->ReadInt32(); + if (region_count != room->RegionCount) + return new Error(String::FromFormat("Mismatching number of regions: read %zu expected %zu", region_count, room->RegionCount)); + int errors = 0; + for (size_t i = 0; i < room->RegionCount; ++i) + { + errors += Properties::ReadValues(room->Regions[i].Properties, in); + } + if (errors > 0) + return new RoomFileError(kRoomFileErr_InvalidPropertyValues); + + size_t wa_count = in->ReadInt32(); + if (wa_count != room->WalkAreaCount) + return new Error(String::FromFormat("Mismatching number of walkable areas: read %zu expected %zu", wa_count, room->WalkAreaCount)); + for (size_t i = 0; i < room->WalkAreaCount; ++i) + { + Properties::ReadValues(room->WalkAreas[i].Properties, in); + } + if (errors > 0) + return new RoomFileError(kRoomFileErr_InvalidPropertyValues); + return HError::None(); +} + HError ReadRoomBlock(RoomStruct *room, Stream *in, RoomFileBlock block, const String &ext_id, soff_t block_len, RoomFileVersion data_ver) { @@ -404,6 +429,10 @@ HError ReadRoomBlock(RoomStruct *room, Stream *in, RoomFileBlock block, const St { return ReadExt_400_WalkOpts(room, in, data_ver); } + else if (ext_id.CompareNoCase("v400_customprops") == 0) + { + return ReadExt_400_CustomProps(room, in, data_ver); + } return new RoomFileError(kRoomFileErr_UnknownBlockType, String::FromFormat("Type: %s", ext_id.GetCStr())); @@ -686,6 +715,21 @@ void WriteExt_400_WalkareaOpts(const RoomStruct *room, Stream *out) } } +void WriteExt_400_CustomProps(const RoomStruct *room, Stream *out) +{ + out->WriteInt32(room->RegionCount); + for (size_t i = 0; i < room->RegionCount; ++i) + { + Properties::WriteValues(room->Regions[i].Properties, out); + } + + out->WriteInt32(room->WalkAreaCount); + for (size_t i = 0; i < room->WalkAreaCount; ++i) + { + Properties::WriteValues(room->WalkAreas[i].Properties, out); + } +} + HRoomFileError WriteRoomData(const RoomStruct *room, Stream *out, RoomFileVersion data_ver) { if (data_ver < kRoomVersion_Current) @@ -716,6 +760,7 @@ HRoomFileError WriteRoomData(const RoomStruct *room, Stream *out, RoomFileVersio // Early development version of "ags4" WriteRoomBlock(room, "ext_ags399", WriteExt399, out); WriteRoomBlock(room, "v400_walkopts", WriteExt_400_WalkareaOpts, out); + WriteRoomBlock(room, "v400_customprops", WriteExt_400_CustomProps, out); // Write end of room file out->WriteByte(kRoomFile_EOF); diff --git a/Common/game/roomstruct.h b/Common/game/roomstruct.h index db7ab870c2..1ff24ec1ab 100644 --- a/Common/game/roomstruct.h +++ b/Common/game/roomstruct.h @@ -205,6 +205,8 @@ struct WalkArea // Top and bottom Y of the area int32_t Top; int32_t Bottom; + // Custom properties + StringIMap Properties; WalkArea(); }; diff --git a/Common/gui/guidefines.h b/Common/gui/guidefines.h index 657357cadf..d1991cd4ef 100644 --- a/Common/gui/guidefines.h +++ b/Common/gui/guidefines.h @@ -150,7 +150,8 @@ enum GuiSvgVersion kGuiSvgVersion_36020, kGuiSvgVersion_36023, kGuiSvgVersion_36025, - kGuiSvgVersion_400 = 4000000 + kGuiSvgVersion_400 = 4000000, + kGuiSvgVersion_40008 = 4000008, // custom properties }; // Style of GUI drawing in disabled state diff --git a/Common/util/string.cpp b/Common/util/string.cpp index 9abb8e85c5..11af0b9bde 100644 --- a/Common/util/string.cpp +++ b/Common/util/string.cpp @@ -154,7 +154,7 @@ void String::Write(Stream *out) const void String::WriteCount(Stream *out, size_t count) const { assert(out); - size_t str_out_len = std::min(count - 1, _len); + size_t str_out_len = std::min(count, _len); if (str_out_len > 0) out->Write(_cstr, str_out_len); size_t null_out_len = count - str_out_len; diff --git a/Common/util/string.h b/Common/util/string.h index 6687c27e1b..27294bc5d2 100644 --- a/Common/util/string.h +++ b/Common/util/string.h @@ -124,8 +124,8 @@ class String void ReadCount(Stream *in, size_t count); // Write() puts the null-terminated string into the stream. void Write(Stream *out) const; - // WriteCount() writes N characters to stream, filling the remaining - // space with null-terminators when needed. + // WriteCount() writes up to N first string's characters to stream, + // filling any of the remaining space with zeroes. void WriteCount(Stream *out, size_t count) const; //------------------------------------------------------------------------- diff --git a/Editor/AGS.Editor.Tests/Types/RoomRegionTests.cs b/Editor/AGS.Editor.Tests/Types/RoomRegionTests.cs index faca67d897..1b1a23bbcc 100644 --- a/Editor/AGS.Editor.Tests/Types/RoomRegionTests.cs +++ b/Editor/AGS.Editor.Tests/Types/RoomRegionTests.cs @@ -1,5 +1,6 @@ using System; using System.Xml; +using NSubstitute; using NUnit.Framework; namespace AGS.Types @@ -7,12 +8,14 @@ namespace AGS.Types [TestFixture] public class RoomRegionTests { + private IChangeNotification _changeNotification; private RoomRegion _roomRegion; [SetUp] public void SetUp() { - _roomRegion = new RoomRegion(); + _changeNotification = Substitute.For(); + _roomRegion = new RoomRegion(_changeNotification); } [TestCase(0)] @@ -117,7 +120,7 @@ public void DeserializesFromXml(bool useColourTint, int lightLevel, int redTint, "; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); - _roomRegion = new RoomRegion(doc.SelectSingleNode("RoomRegion")); + _roomRegion = new RoomRegion(_changeNotification, doc.SelectSingleNode("RoomRegion")); Assert.That(_roomRegion.UseColourTint, Is.EqualTo(useColourTint)); Assert.That(_roomRegion.LightLevel, Is.EqualTo(lightLevel)); diff --git a/Editor/AGS.Editor.Tests/Types/RoomWalkableAreaTests.cs b/Editor/AGS.Editor.Tests/Types/RoomWalkableAreaTests.cs index 2f24e6b1a3..d03258cdcd 100644 --- a/Editor/AGS.Editor.Tests/Types/RoomWalkableAreaTests.cs +++ b/Editor/AGS.Editor.Tests/Types/RoomWalkableAreaTests.cs @@ -1,17 +1,20 @@ -using NUnit.Framework; -using System.Xml; +using System.Xml; +using NSubstitute; +using NUnit.Framework; namespace AGS.Types { [TestFixture] public class RoomWalkableAreaTests { + private IChangeNotification _changeNotification; private RoomWalkableArea _roomWalkAbleArea; [SetUp] public void SetUp() { - _roomWalkAbleArea = new RoomWalkableArea(); + _changeNotification = Substitute.For(); + _roomWalkAbleArea = new RoomWalkableArea(_changeNotification); } [TestCase(0)] @@ -90,7 +93,7 @@ public void DeserializesFromXml(int areaSpecificView, bool userContinousScaling, "; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); - _roomWalkAbleArea = new RoomWalkableArea(doc.SelectSingleNode("RoomWalkableArea")); + _roomWalkAbleArea = new RoomWalkableArea(_changeNotification, doc.SelectSingleNode("RoomWalkableArea")); Assert.That(_roomWalkAbleArea.AreaSpecificView, Is.EqualTo(areaSpecificView)); Assert.That(_roomWalkAbleArea.UseContinuousScaling, Is.EqualTo(userContinousScaling)); diff --git a/Editor/AGS.Editor/AGSEditor.cs b/Editor/AGS.Editor/AGSEditor.cs index 55a5d2d31e..2b9ff67383 100644 --- a/Editor/AGS.Editor/AGSEditor.cs +++ b/Editor/AGS.Editor/AGSEditor.cs @@ -114,9 +114,11 @@ public class AGSEditor : IAGSEditorDirectories * 4.00.00.03 - Distinct Character and Object Enabled and Visible properties; * - FaceDirectionRatio * 4.00.00.07 - Sprite.ImportColorDepth + * 4.00.00.08 - Custom properties supported by AudioClips, Dialogs, GUI, + * Regions, Walkable Areas * */ - public const int LATEST_XML_VERSION_INDEX = 4000007; + public const int LATEST_XML_VERSION_INDEX = 4000008; /// /// XML version index on the release of AGS 4.0.0, this constant be used to determine /// if upgrade of Rooms/Sprites/etc. to new format have been performed. diff --git a/Editor/AGS.Editor/DataFileWriter.cs b/Editor/AGS.Editor/DataFileWriter.cs index ad134d4005..e3fab4d854 100644 --- a/Editor/AGS.Editor/DataFileWriter.cs +++ b/Editor/AGS.Editor/DataFileWriter.cs @@ -1725,6 +1725,7 @@ public static bool SaveThisGameToFile(string fileName, Game game, CompileMessage WriteExtension("v361_objnames", WriteExt_361ObjNames, writer, game, errors); WriteExtension("ext_ags399", WriteExt_Ags399, writer, game, errors); WriteExtension("v400_gameopts", WriteExt_400GameOpts, writer, game, errors); + WriteExtension("v400_customprops", WriteExt_400CustomProps, writer, game, errors); // End of extensions list writer.Write((byte)0xff); @@ -1831,6 +1832,28 @@ private static void WriteExt_400GameOpts(BinaryWriter writer, Game game, Compile writer.Write((int)0); } + private static void WriteExt_400CustomProps(BinaryWriter writer, Game game, CompileMessages errors) + { + // Audio clip properties + writer.Write((int)game.AudioClips.Count); + for (int i = 0; i < game.AudioClips.Count; ++i) + { + CustomPropertiesWriter.Write(writer, game.AudioClips[i].Properties); + } + // Dialog properties + writer.Write((int)game.Dialogs.Count); + for (int i = 0; i < game.Dialogs.Count; ++i) + { + CustomPropertiesWriter.Write(writer, game.Dialogs[i].Properties); + } + // GUI properties + writer.Write((int)game.GUIs.Count); + for (int i = 0; i < game.GUIs.Count; ++i) + { + CustomPropertiesWriter.Write(writer, game.GUIs[i].Properties); + } + } + private delegate void WriteExtensionProc(BinaryWriter writer, Game game, CompileMessages errors); private static void WriteExtension(string ext_id, WriteExtensionProc proc, BinaryWriter writer, Game game, CompileMessages errors) diff --git a/Editor/AGS.Editor/GUI/CustomPropertyBag.cs b/Editor/AGS.Editor/GUI/CustomPropertyBag.cs index a4d05bf170..bc65c60ad3 100644 --- a/Editor/AGS.Editor/GUI/CustomPropertyBag.cs +++ b/Editor/AGS.Editor/GUI/CustomPropertyBag.cs @@ -69,11 +69,7 @@ public PropertyDescriptorCollection GetProperties(Attribute[] attributes) List descriptors = new List(); foreach (CustomPropertySchemaItem item in _schema.PropertyDefinitions) { - if (((_showPropertiesThatApplyTo == CustomPropertyAppliesTo.Characters) && (item.AppliesToCharacters)) || - ((_showPropertiesThatApplyTo == CustomPropertyAppliesTo.Hotspots) && (item.AppliesToHotspots)) || - ((_showPropertiesThatApplyTo == CustomPropertyAppliesTo.InventoryItems) && (item.AppliesToInvItems)) || - ((_showPropertiesThatApplyTo == CustomPropertyAppliesTo.Objects) && (item.AppliesToObjects)) || - ((_showPropertiesThatApplyTo == CustomPropertyAppliesTo.Rooms) && (item.AppliesToRooms))) + if ((_showPropertiesThatApplyTo & item.AppliesTo) != 0) { PropertyDescriptor descriptor = new CustomPropertyDescriptor(item, _properties); descriptors.Add(descriptor); diff --git a/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.Designer.cs b/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.Designer.cs index 91bb909166..25bccfd8be 100644 --- a/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.Designer.cs +++ b/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.Designer.cs @@ -39,12 +39,17 @@ private void InitializeComponent() this.btnOK = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button(); this.groupBox1 = new System.Windows.Forms.GroupBox(); - this.chkCharacters = new System.Windows.Forms.CheckBox(); - this.chkObjects = new System.Windows.Forms.CheckBox(); + this.chkWalkareas = new System.Windows.Forms.CheckBox(); + this.chkRegions = new System.Windows.Forms.CheckBox(); + this.chkAudioClips = new System.Windows.Forms.CheckBox(); + this.chkGUIs = new System.Windows.Forms.CheckBox(); + this.chkDialogs = new System.Windows.Forms.CheckBox(); + this.chkRooms = new System.Windows.Forms.CheckBox(); + this.label5 = new System.Windows.Forms.Label(); this.chkInventory = new System.Windows.Forms.CheckBox(); this.chkHotspots = new System.Windows.Forms.CheckBox(); - this.label5 = new System.Windows.Forms.Label(); - this.chkRooms = new System.Windows.Forms.CheckBox(); + this.chkObjects = new System.Windows.Forms.CheckBox(); + this.chkCharacters = new System.Windows.Forms.CheckBox(); this.groupBox1.SuspendLayout(); this.SuspendLayout(); // @@ -120,7 +125,8 @@ private void InitializeComponent() // // btnOK // - this.btnOK.Location = new System.Drawing.Point(15, 211); + this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnOK.Location = new System.Drawing.Point(15, 249); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(92, 26); this.btnOK.TabIndex = 8; @@ -130,8 +136,9 @@ private void InitializeComponent() // // btnCancel // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.btnCancel.Location = new System.Drawing.Point(129, 211); + this.btnCancel.Location = new System.Drawing.Point(129, 249); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(92, 26); this.btnCancel.TabIndex = 9; @@ -141,6 +148,14 @@ private void InitializeComponent() // // groupBox1 // + this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox1.Controls.Add(this.chkWalkareas); + this.groupBox1.Controls.Add(this.chkRegions); + this.groupBox1.Controls.Add(this.chkAudioClips); + this.groupBox1.Controls.Add(this.chkGUIs); + this.groupBox1.Controls.Add(this.chkDialogs); this.groupBox1.Controls.Add(this.chkRooms); this.groupBox1.Controls.Add(this.label5); this.groupBox1.Controls.Add(this.chkInventory); @@ -149,35 +164,84 @@ private void InitializeComponent() this.groupBox1.Controls.Add(this.chkCharacters); this.groupBox1.Location = new System.Drawing.Point(15, 129); this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(461, 66); + this.groupBox1.Size = new System.Drawing.Size(461, 104); this.groupBox1.TabIndex = 11; this.groupBox1.TabStop = false; this.groupBox1.Text = "Applies To"; // - // chkCharacters + // chkWalkareas + // + this.chkWalkareas.AutoSize = true; + this.chkWalkareas.Location = new System.Drawing.Point(316, 68); + this.chkWalkareas.Name = "chkWalkareas"; + this.chkWalkareas.Size = new System.Drawing.Size(100, 17); + this.chkWalkareas.TabIndex = 21; + this.chkWalkareas.Text = "Walkable Areas"; + this.chkWalkareas.UseVisualStyleBackColor = true; + // + // chkRegions + // + this.chkRegions.AutoSize = true; + this.chkRegions.Location = new System.Drawing.Point(246, 68); + this.chkRegions.Name = "chkRegions"; + this.chkRegions.Size = new System.Drawing.Size(64, 17); + this.chkRegions.TabIndex = 20; + this.chkRegions.Text = "Regions"; + this.chkRegions.UseVisualStyleBackColor = true; + // + // chkAudioClips + // + this.chkAudioClips.AutoSize = true; + this.chkAudioClips.Location = new System.Drawing.Point(358, 41); + this.chkAudioClips.Name = "chkAudioClips"; + this.chkAudioClips.Size = new System.Drawing.Size(78, 17); + this.chkAudioClips.TabIndex = 19; + this.chkAudioClips.Text = "Audio Clips"; + this.chkAudioClips.UseVisualStyleBackColor = true; + // + // chkGUIs + // + this.chkGUIs.AutoSize = true; + this.chkGUIs.Location = new System.Drawing.Point(175, 41); + this.chkGUIs.Name = "chkGUIs"; + this.chkGUIs.Size = new System.Drawing.Size(49, 17); + this.chkGUIs.TabIndex = 18; + this.chkGUIs.Text = "GUIs"; + this.chkGUIs.UseVisualStyleBackColor = true; + // + // chkDialogs + // + this.chkDialogs.AutoSize = true; + this.chkDialogs.Location = new System.Drawing.Point(98, 41); + this.chkDialogs.Name = "chkDialogs"; + this.chkDialogs.Size = new System.Drawing.Size(60, 17); + this.chkDialogs.TabIndex = 17; + this.chkDialogs.Text = "Dialogs"; + this.chkDialogs.UseVisualStyleBackColor = true; // - this.chkCharacters.AutoSize = true; - this.chkCharacters.Location = new System.Drawing.Point(13, 41); - this.chkCharacters.Name = "chkCharacters"; - this.chkCharacters.Size = new System.Drawing.Size(79, 17); - this.chkCharacters.TabIndex = 11; - this.chkCharacters.Text = "Characters"; - this.chkCharacters.UseVisualStyleBackColor = true; + // chkRooms // - // chkObjects + this.chkRooms.AutoSize = true; + this.chkRooms.Location = new System.Drawing.Point(13, 68); + this.chkRooms.Name = "chkRooms"; + this.chkRooms.Size = new System.Drawing.Size(58, 17); + this.chkRooms.TabIndex = 16; + this.chkRooms.Text = "Rooms"; + this.chkRooms.UseVisualStyleBackColor = true; // - this.chkObjects.AutoSize = true; - this.chkObjects.Location = new System.Drawing.Point(313, 41); - this.chkObjects.Name = "chkObjects"; - this.chkObjects.Size = new System.Drawing.Size(63, 17); - this.chkObjects.TabIndex = 12; - this.chkObjects.Text = "Objects"; - this.chkObjects.UseVisualStyleBackColor = true; + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(10, 19); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(294, 13); + this.label5.TabIndex = 15; + this.label5.Text = "Which types of thing do you want this property to apply to?"; // // chkInventory // this.chkInventory.AutoSize = true; - this.chkInventory.Location = new System.Drawing.Point(193, 41); + this.chkInventory.Location = new System.Drawing.Point(246, 41); this.chkInventory.Name = "chkInventory"; this.chkInventory.Size = new System.Drawing.Size(104, 17); this.chkInventory.TabIndex = 14; @@ -187,31 +251,32 @@ private void InitializeComponent() // chkHotspots // this.chkHotspots.AutoSize = true; - this.chkHotspots.Location = new System.Drawing.Point(108, 41); + this.chkHotspots.Location = new System.Drawing.Point(175, 68); this.chkHotspots.Name = "chkHotspots"; this.chkHotspots.Size = new System.Drawing.Size(69, 17); this.chkHotspots.TabIndex = 13; this.chkHotspots.Text = "Hotspots"; this.chkHotspots.UseVisualStyleBackColor = true; // - // label5 + // chkObjects // - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(10, 19); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(294, 13); - this.label5.TabIndex = 15; - this.label5.Text = "Which types of thing do you want this property to apply to?"; + this.chkObjects.AutoSize = true; + this.chkObjects.Location = new System.Drawing.Point(98, 68); + this.chkObjects.Name = "chkObjects"; + this.chkObjects.Size = new System.Drawing.Size(63, 17); + this.chkObjects.TabIndex = 12; + this.chkObjects.Text = "Objects"; + this.chkObjects.UseVisualStyleBackColor = true; // - // chkRooms + // chkCharacters // - this.chkRooms.AutoSize = true; - this.chkRooms.Location = new System.Drawing.Point(392, 41); - this.chkRooms.Name = "chkRooms"; - this.chkRooms.Size = new System.Drawing.Size(58, 17); - this.chkRooms.TabIndex = 16; - this.chkRooms.Text = "Rooms"; - this.chkRooms.UseVisualStyleBackColor = true; + this.chkCharacters.AutoSize = true; + this.chkCharacters.Location = new System.Drawing.Point(13, 41); + this.chkCharacters.Name = "chkCharacters"; + this.chkCharacters.Size = new System.Drawing.Size(79, 17); + this.chkCharacters.TabIndex = 11; + this.chkCharacters.Text = "Characters"; + this.chkCharacters.UseVisualStyleBackColor = true; // // CustomPropertySchemaItemEditor // @@ -219,7 +284,7 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.CancelButton = this.btnCancel; - this.ClientSize = new System.Drawing.Size(490, 249); + this.ClientSize = new System.Drawing.Size(490, 287); this.Controls.Add(this.groupBox1); this.Controls.Add(this.btnCancel); this.Controls.Add(this.btnOK); @@ -264,5 +329,10 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox chkObjects; private System.Windows.Forms.CheckBox chkCharacters; private System.Windows.Forms.CheckBox chkRooms; + private System.Windows.Forms.CheckBox chkGUIs; + private System.Windows.Forms.CheckBox chkDialogs; + private System.Windows.Forms.CheckBox chkAudioClips; + private System.Windows.Forms.CheckBox chkWalkareas; + private System.Windows.Forms.CheckBox chkRegions; } } \ No newline at end of file diff --git a/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.cs b/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.cs index 0e49543ace..0e2b050144 100644 --- a/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.cs +++ b/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.cs @@ -28,6 +28,11 @@ public CustomPropertySchemaItemEditor(CustomPropertySchemaItem item, bool isNewI chkInventory.DataBindings.Add("Checked", _copyOfItem, "AppliesToInvItems", true, DataSourceUpdateMode.OnPropertyChanged); chkObjects.DataBindings.Add("Checked", _copyOfItem, "AppliesToObjects", true, DataSourceUpdateMode.OnPropertyChanged); chkRooms.DataBindings.Add("Checked", _copyOfItem, "AppliesToRooms", true, DataSourceUpdateMode.OnPropertyChanged); + chkAudioClips.DataBindings.Add("Checked", _copyOfItem, "AppliesToAudioClips", true, DataSourceUpdateMode.OnPropertyChanged); + chkDialogs.DataBindings.Add("Checked", _copyOfItem, "AppliesToDialogs", true, DataSourceUpdateMode.OnPropertyChanged); + chkGUIs.DataBindings.Add("Checked", _copyOfItem, "AppliesToGUIs", true, DataSourceUpdateMode.OnPropertyChanged); + chkRegions.DataBindings.Add("Checked", _copyOfItem, "AppliesToRegions", true, DataSourceUpdateMode.OnPropertyChanged); + chkWalkareas.DataBindings.Add("Checked", _copyOfItem, "AppliesToWalkableAreas", true, DataSourceUpdateMode.OnPropertyChanged); cmbType.SelectedIndex = ((int)_copyOfItem.Type) - 1; if (!isNewItem) @@ -67,11 +72,7 @@ private void btnOK_Click(object sender, EventArgs e) _itemToEdit.Description = _copyOfItem.Description; _itemToEdit.DefaultValue = _copyOfItem.DefaultValue; _itemToEdit.Type = propertyType; - _itemToEdit.AppliesToCharacters = _copyOfItem.AppliesToCharacters; - _itemToEdit.AppliesToHotspots = _copyOfItem.AppliesToHotspots; - _itemToEdit.AppliesToInvItems = _copyOfItem.AppliesToInvItems; - _itemToEdit.AppliesToObjects = _copyOfItem.AppliesToObjects; - _itemToEdit.AppliesToRooms = _copyOfItem.AppliesToRooms; + _itemToEdit.AppliesTo = _copyOfItem.AppliesTo; this.DialogResult = DialogResult.OK; this.Close(); } diff --git a/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.resx b/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.resx index ff31a6db56..c7e0d4bdf1 100644 --- a/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.resx +++ b/Editor/AGS.Editor/GUI/CustomPropertySchemaItemEditor.resx @@ -112,9 +112,9 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/Editor/AGS.Editor/GUI/GUIController.cs b/Editor/AGS.Editor/GUI/GUIController.cs index e7b7696ea8..9d35caeab6 100644 --- a/Editor/AGS.Editor/GUI/GUIController.cs +++ b/Editor/AGS.Editor/GUI/GUIController.cs @@ -1476,22 +1476,42 @@ private void ShowPropertiesEditorFromPropertyGrid(CustomProperties props, object { propertyTypes = CustomPropertyAppliesTo.Characters; } - else if (objectThatHasProperties is RoomHotspot) + else if (objectThatHasProperties is Dialog) { - propertyTypes = CustomPropertyAppliesTo.Hotspots; + propertyTypes = CustomPropertyAppliesTo.Dialogs; + } + else if (objectThatHasProperties is GUI) + { + propertyTypes = CustomPropertyAppliesTo.GUIs; } else if (objectThatHasProperties is InventoryItem) { propertyTypes = CustomPropertyAppliesTo.InventoryItems; } - else if (objectThatHasProperties is RoomObject) + else if (objectThatHasProperties is AudioClip) { - propertyTypes = CustomPropertyAppliesTo.Objects; + propertyTypes = CustomPropertyAppliesTo.AudioClips; } else if (objectThatHasProperties is Room) { propertyTypes = CustomPropertyAppliesTo.Rooms; } + else if (objectThatHasProperties is RoomObject) + { + propertyTypes = CustomPropertyAppliesTo.Objects; + } + else if (objectThatHasProperties is RoomHotspot) + { + propertyTypes = CustomPropertyAppliesTo.Hotspots; + } + else if (objectThatHasProperties is RoomRegion) + { + propertyTypes = CustomPropertyAppliesTo.Regions; + } + else if (objectThatHasProperties is RoomWalkableArea) + { + propertyTypes = CustomPropertyAppliesTo.WalkableAreas; + } else { throw new AGSEditorException("Object not recognised to contain properties: " + objectThatHasProperties.ToString()); diff --git a/Editor/AGS.Editor/Resources/agsdefns.sh b/Editor/AGS.Editor/Resources/agsdefns.sh index 9126477c9b..1a3a56b922 100644 --- a/Editor/AGS.Editor/Resources/agsdefns.sh +++ b/Editor/AGS.Editor/Resources/agsdefns.sh @@ -1653,6 +1653,15 @@ builtin managed struct GUI { import Point *GUIToScreenPoint(int guix, int guiy, bool clipToGUI = true); import Point *ScreenToGUIPoint(int screenx, int screeny, bool clipToGUI = true); + + /// Gets an integer custom property for this GUI. + import int GetProperty(const string property); + /// Gets a text custom property for this GUI. + import String GetTextProperty(const string property); + /// Sets an integer custom property for this GUI. + import bool SetProperty(const string property, int value); + /// Sets a text custom property for this GUI. + import bool SetTextProperty(const string property, const string value); #endif readonly int reserved[2]; // $AUTOCOMPLETEIGNORE$ }; @@ -1742,6 +1751,16 @@ builtin managed struct Region { #ifdef SCRIPT_API_v360 /// Gets the drawing surface for the 8-bit regions mask import static DrawingSurface* GetDrawingSurface(); // $AUTOCOMPLETESTATICONLY$ +#endif +#ifdef SCRIPT_API_v400 + /// Gets an integer custom property for this region. + import int GetProperty(const string property); + /// Gets a text custom property for this region. + import String GetTextProperty(const string property); + /// Sets an integer custom property for this region. + import bool SetProperty(const string property, int value); + /// Sets a text custom property for this region. + import bool SetTextProperty(const string property, const string value); #endif int reserved[2]; // $AUTOCOMPLETEIGNORE$ }; @@ -1756,6 +1775,15 @@ builtin managed struct WalkableArea { import static DrawingSurface* GetDrawingSurface(); // $AUTOCOMPLETESTATICONLY$ /// Changes this walkable area's scaling level. import void SetScaling(int min, int max); + /// Gets an integer custom property for this area. + import int GetProperty(const string property); + /// Gets a text custom property for this area. + import String GetTextProperty(const string property); + /// Sets an integer custom property for this area. + import bool SetProperty(const string property, int value); + /// Sets a text custom property for this area. + import bool SetTextProperty(const string property, const string value); + /// Gets the ID number for this area. import readonly attribute int ID; /// Gets/sets whether this walkable area is enabled. @@ -1819,6 +1847,14 @@ builtin managed struct Dialog { import static readonly attribute int ExecutedOption; // $AUTOCOMPLETESTATICONLY$ /// Gets if the dialog options are currently displayed on screen import static readonly attribute bool AreOptionsDisplayed; // $AUTOCOMPLETESTATICONLY$ + /// Gets an integer custom property for this dialog. + import int GetProperty(const string property); + /// Gets a text custom property for this dialog. + import String GetTextProperty(const string property); + /// Sets an integer custom property for this dialog. + import bool SetProperty(const string property, int value); + /// Sets a text custom property for this dialog. + import bool SetTextProperty(const string property, const string value); #endif readonly int reserved[2]; // $AUTOCOMPLETEIGNORE$ @@ -1981,6 +2017,16 @@ builtin managed struct AudioClip { /// Gets the script name of this clip. import readonly attribute String ScriptName; #endif +#ifdef SCRIPT_API_v400 + /// Gets an integer custom property for this item. + import int GetProperty(const string property); + /// Gets a text custom property for this item. + import String GetTextProperty(const string property); + /// Sets an integer custom property for this item. + import bool SetProperty(const string property, int value); + /// Sets a text custom property for this item. + import bool SetTextProperty(const string property, const string value); +#endif }; diff --git a/Editor/AGS.Native/agsnative.cpp b/Editor/AGS.Native/agsnative.cpp index 25cb5589fd..59bb844abe 100644 --- a/Editor/AGS.Native/agsnative.cpp +++ b/Editor/AGS.Native/agsnative.cpp @@ -2797,8 +2797,7 @@ void convert_room_from_native(const RoomStruct &rs, Room ^room, System::Text::En obj->UseRoomAreaScaling = ((rs.Objects[i].Flags & OBJF_USEROOMSCALING) != 0); obj->UseRoomAreaLighting = ((rs.Objects[i].Flags & OBJF_USEREGIONTINTS) != 0); ConvertCustomProperties(obj->Properties, &rs.Objects[i].Properties); - - CopyInteractions(obj->Interactions, rs.Objects[i].EventHandlers.get()); + CopyInteractions(obj->Interactions, rs.Objects[i].EventHandlers.get()); room->Objects->Add(obj); } @@ -2810,9 +2809,8 @@ void convert_room_from_native(const RoomStruct &rs, Room ^room, System::Text::En hotspot->Description = tcv->Convert(rs.Hotspots[i].Name); hotspot->Name = TextHelper::ConvertASCII(rs.Hotspots[i].ScriptName); hotspot->WalkToPoint = System::Drawing::Point(rs.Hotspots[i].WalkTo.X, rs.Hotspots[i].WalkTo.Y); - ConvertCustomProperties(hotspot->Properties, &rs.Hotspots[i].Properties); - - CopyInteractions(hotspot->Interactions, rs.Hotspots[i].EventHandlers.get()); + CopyInteractions(hotspot->Interactions, rs.Hotspots[i].EventHandlers.get()); + ConvertCustomProperties(hotspot->Properties, &rs.Hotspots[i].Properties); } for (size_t i = 0; i < MAX_WALK_AREAS; ++i) @@ -2832,6 +2830,7 @@ void convert_room_from_native(const RoomStruct &rs, Room ^room, System::Text::En { area->MaxScalingLevel = area->MinScalingLevel; } + ConvertCustomProperties(area->Properties, &rs.WalkAreas[i].Properties); } for (size_t i = 0; i < MAX_WALK_BEHINDS; ++i) @@ -2861,11 +2860,11 @@ void convert_room_from_native(const RoomStruct &rs, Room ^room, System::Text::En area->TintLuminance = area->UseColourTint ? luminance : Utilities::GetDefaultValue(area->GetType(), "TintLuminance", 0); - CopyInteractions(area->Interactions, rs.Regions[i].EventHandlers.get()); + ConvertCustomProperties(area->Properties, &rs.Regions[i].Properties); + CopyInteractions(area->Interactions, rs.Regions[i].EventHandlers.get()); } ConvertCustomProperties(room->Properties, &rs.Properties); - CopyInteractions(room->Interactions, rs.EventHandlers.get()); } @@ -2944,6 +2943,8 @@ void convert_room_to_native(Room ^room, RoomStruct &rs) rs.WalkAreas[i].ScalingFar = area->ScalingLevel - 100; rs.WalkAreas[i].ScalingNear = NOT_VECTOR_SCALED; } + + CompileCustomProperties(area->Properties, &rs.WalkAreas[i].Properties); } rs.WalkBehindCount = room->WalkBehinds->Count; @@ -2970,6 +2971,8 @@ void convert_room_to_native(Room ^room, RoomStruct &rs) // for compatibility with older versions of the editor. rs.Regions[i].Light = area->LightLevel - 100; } + + CompileCustomProperties(area->Properties, &rs.Regions[i].Properties); } CompileCustomProperties(room->Properties, &rs.Properties); diff --git a/Editor/AGS.Types/AudioClip.cs b/Editor/AGS.Types/AudioClip.cs index 9014e9c57c..1a79a428a9 100644 --- a/Editor/AGS.Types/AudioClip.cs +++ b/Editor/AGS.Types/AudioClip.cs @@ -27,6 +27,7 @@ public class AudioClip : IToXml, IComparable private int _actualVolume; private AudioClipPriority _actualPriority; private bool _actualRepeat; + private CustomProperties _properties = new CustomProperties(); // The value of a "no sound reference" public const int FixedIndexNoValue = 0; @@ -187,6 +188,16 @@ public bool ActualRepeat set { _actualRepeat = value; } } + [AGSSerializeClass()] + [Description("Custom properties for this audio clip")] + [Category("Properties")] + [EditorAttribute(typeof(CustomPropertiesUIEditor), typeof(System.Drawing.Design.UITypeEditor))] + public CustomProperties Properties + { + get { return _properties; } + protected set { _properties = value; } + } + public AudioClip(XmlNode node) { SerializeUtils.DeserializeFromXML(this, node); diff --git a/Editor/AGS.Types/Character.cs b/Editor/AGS.Types/Character.cs index b26f553a6f..2e6d675599 100644 --- a/Editor/AGS.Types/Character.cs +++ b/Editor/AGS.Types/Character.cs @@ -47,7 +47,7 @@ public class Character : ICustomTypeDescriptor, IToXml, IComparable private bool _adjustSpeedWithScaling; private bool _adjustVolumeWithScaling; private bool _movementLinkedToAnimation = true; - private CustomProperties _properties; + private CustomProperties _properties = new CustomProperties(); private Interactions _interactions = new Interactions(_interactionSchema); static Character() @@ -62,7 +62,6 @@ static Character() public Character() { - _properties = new CustomProperties(); } [Description("The ID number of the character")] diff --git a/Editor/AGS.Types/CustomPropertySchemaItem.cs b/Editor/AGS.Types/CustomPropertySchemaItem.cs index 79972969db..1ee938d9c2 100644 --- a/Editor/AGS.Types/CustomPropertySchemaItem.cs +++ b/Editor/AGS.Types/CustomPropertySchemaItem.cs @@ -11,17 +11,14 @@ public class CustomPropertySchemaItem : ICloneable private string _description; private string _defaultValue; private CustomPropertyType _type; - private bool _appliesToCharacters = true; - private bool _appliesToHotspots = true; - private bool _appliesToObjects = true; - private bool _appliesToInvItems = true; - private bool _appliesToRooms = true; + private CustomPropertyAppliesTo _appliesTo; public CustomPropertySchemaItem() { _name = string.Empty; _description = string.Empty; _defaultValue = string.Empty; + _appliesTo = CustomPropertyAppliesTo.Everything; } public string Name @@ -42,34 +39,71 @@ public string DefaultValue set { _defaultValue = value; } } + [AGSNoSerialize] + public CustomPropertyAppliesTo AppliesTo + { + get { return _appliesTo; } + set { _appliesTo = value; } + } + public bool AppliesToCharacters { - get { return _appliesToCharacters; } - set { _appliesToCharacters = value; } + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.Characters); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.Characters) : (_appliesTo & ~CustomPropertyAppliesTo.Characters); } } public bool AppliesToHotspots { - get { return _appliesToHotspots; } - set { _appliesToHotspots = value; } + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.Hotspots); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.Hotspots) : (_appliesTo & ~CustomPropertyAppliesTo.Hotspots); } } public bool AppliesToInvItems { - get { return _appliesToInvItems; } - set { _appliesToInvItems = value; } + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.InventoryItems); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.InventoryItems) : (_appliesTo & ~CustomPropertyAppliesTo.InventoryItems); } } public bool AppliesToObjects { - get { return _appliesToObjects; } - set { _appliesToObjects = value; } + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.Objects); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.Objects) : (_appliesTo & ~CustomPropertyAppliesTo.Objects); } } public bool AppliesToRooms { - get { return _appliesToRooms; } - set { _appliesToRooms = value; } + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.Rooms); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.Rooms) : (_appliesTo & ~CustomPropertyAppliesTo.Rooms); } + } + + public bool AppliesToAudioClips + { + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.AudioClips); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.AudioClips) : (_appliesTo & ~CustomPropertyAppliesTo.AudioClips); } + } + + public bool AppliesToDialogs + { + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.Dialogs); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.Dialogs) : (_appliesTo & ~CustomPropertyAppliesTo.Dialogs); } + } + + public bool AppliesToGUIs + { + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.GUIs); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.GUIs) : (_appliesTo & ~CustomPropertyAppliesTo.GUIs); } + } + + public bool AppliesToRegions + { + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.Regions); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.Regions) : (_appliesTo & ~CustomPropertyAppliesTo.Regions); } + } + + public bool AppliesToWalkableAreas + { + get { return _appliesTo.HasFlag(CustomPropertyAppliesTo.WalkableAreas); } + set { _appliesTo = value ? (_appliesTo | CustomPropertyAppliesTo.WalkableAreas) : (_appliesTo & ~CustomPropertyAppliesTo.WalkableAreas); } } public CustomPropertyType Type @@ -84,11 +118,16 @@ public string AppliesToAsString get { string toReturn = string.Empty; - toReturn += _appliesToCharacters ? "C" : " "; - toReturn += _appliesToHotspots ? "H" : " "; - toReturn += _appliesToInvItems ? "I" : " "; - toReturn += _appliesToObjects ? "O" : " "; - toReturn += _appliesToRooms ? "R" : " "; + toReturn += AppliesToAudioClips ? "A" : " "; + toReturn += AppliesToCharacters ? "C" : " "; + toReturn += AppliesToDialogs ? "D" : " "; + toReturn += AppliesToGUIs ? "G" : " "; + toReturn += AppliesToInvItems ? "I" : " "; + toReturn += AppliesToRooms ? "R" : " "; + toReturn += AppliesToHotspots ? "H" : " "; + toReturn += AppliesToObjects ? "O" : " "; + toReturn += AppliesToRegions ? "Rg" : " "; + toReturn += AppliesToWalkableAreas ? "W" : " "; return toReturn; } } @@ -114,6 +153,7 @@ public object GetTypedDefaultValue() public CustomPropertySchemaItem(XmlNode node) { + this.AppliesTo = CustomPropertyAppliesTo.None; // reset before reading back SerializeUtils.DeserializeFromXML(this, node); } @@ -129,11 +169,7 @@ public object Clone() copy.Description = this.Description; copy.Name = this.Name; copy.Type = this.Type; - copy.AppliesToCharacters = this.AppliesToCharacters; - copy.AppliesToHotspots = this.AppliesToHotspots; - copy.AppliesToInvItems = this.AppliesToInvItems; - copy.AppliesToObjects = this.AppliesToObjects; - copy.AppliesToRooms = this.AppliesToRooms; + copy.AppliesTo = this.AppliesTo; return copy; } } diff --git a/Editor/AGS.Types/Dialog.cs b/Editor/AGS.Types/Dialog.cs index 014de0ec37..dfb8d64cc4 100644 --- a/Editor/AGS.Types/Dialog.cs +++ b/Editor/AGS.Types/Dialog.cs @@ -19,6 +19,7 @@ public class Dialog : IScript, IToXml, IComparable private bool _scriptChangedSinceLastCompile; private string _cachedConvertedScript; private List _options = new List(); + private CustomProperties _properties = new CustomProperties(); public Dialog() { @@ -104,6 +105,16 @@ public string WindowTitle get { return string.IsNullOrEmpty(this.Name) ? ("Dialog " + this.ID) : ("Dialog: " + this.Name); } } + [AGSSerializeClass()] + [Description("Custom properties for this dialog")] + [Category("Properties")] + [EditorAttribute(typeof(CustomPropertiesUIEditor), typeof(System.Drawing.Design.UITypeEditor))] + public CustomProperties Properties + { + get { return _properties; } + protected set { _properties = value; } + } + public Dialog(XmlNode node) { _scriptChangedSinceLastCompile = true; diff --git a/Editor/AGS.Types/Enums/CustomPropertyAppliesTo.cs b/Editor/AGS.Types/Enums/CustomPropertyAppliesTo.cs index c54df3a9dd..3cf02a3b78 100644 --- a/Editor/AGS.Types/Enums/CustomPropertyAppliesTo.cs +++ b/Editor/AGS.Types/Enums/CustomPropertyAppliesTo.cs @@ -4,12 +4,20 @@ namespace AGS.Types { + [Flags] public enum CustomPropertyAppliesTo { - Characters, - Hotspots, - InventoryItems, - Objects, - Rooms + None = 0, + Characters = 0x00000001, + Hotspots = 0x00000002, + InventoryItems = 0x00000004, + Objects = 0x00000008, + Rooms = 0x00000010, + AudioClips = 0x00000020, + Dialogs = 0x00000040, + GUIs = 0x00000080, + Regions = 0x00000100, + WalkableAreas = 0x00000200, + Everything = 0x0FFFFFFF } } diff --git a/Editor/AGS.Types/GUI.cs b/Editor/AGS.Types/GUI.cs index 3018d2ca79..b770cfb86a 100644 --- a/Editor/AGS.Types/GUI.cs +++ b/Editor/AGS.Types/GUI.cs @@ -12,17 +12,18 @@ namespace AGS.Types [DefaultProperty("BackgroundImage")] public abstract class GUI : IToXml, IComparable { - public GUI() - { - _name = string.Empty; - _bgcol = 8; - } - protected string _name; protected int _id; protected int _bgcol; protected int _bgimage; protected List _controls = new List(); + private CustomProperties _properties = new CustomProperties(); + + public GUI() + { + _name = string.Empty; + _bgcol = 8; + } /// /// Width of the GUI, as displayed in the Editor. @@ -131,6 +132,16 @@ public List Controls set { _controls = value; } } + [AGSSerializeClass()] + [Description("Custom properties for this GUI")] + [Category("Properties")] + [EditorAttribute(typeof(CustomPropertiesUIEditor), typeof(System.Drawing.Design.UITypeEditor))] + public CustomProperties Properties + { + get { return _properties; } + protected set { _properties = value; } + } + public void SendControlToBack(GUIControl controlToSend) { int currentZOrder = controlToSend.ZOrder; diff --git a/Editor/AGS.Types/InventoryItem.cs b/Editor/AGS.Types/InventoryItem.cs index 6572e5f9f9..a605e09042 100644 --- a/Editor/AGS.Types/InventoryItem.cs +++ b/Editor/AGS.Types/InventoryItem.cs @@ -19,7 +19,7 @@ public class InventoryItem : IToXml, IComparable private bool _startWithItem; private int _id; private int _hotspotX, _hotspotY; - private CustomProperties _properties; + private CustomProperties _properties = new CustomProperties(); private Interactions _interactions = new Interactions(_interactionSchema); private bool _currentlyDeserializing = false; @@ -41,7 +41,6 @@ public InventoryItem() _cursorImage = 0; _hotspotX = 0; _hotspotY = 0; - _properties = new CustomProperties(); } [Description("The ID number of the item")] diff --git a/Editor/AGS.Types/Room.cs b/Editor/AGS.Types/Room.cs index 145b5b75eb..87ff742a4e 100644 --- a/Editor/AGS.Types/Room.cs +++ b/Editor/AGS.Types/Room.cs @@ -89,7 +89,7 @@ public Room(int roomNumber) : base(roomNumber) for (int i = 0; i < MAX_WALKABLE_AREAS; i++) { - RoomWalkableArea area = new RoomWalkableArea(); + RoomWalkableArea area = new RoomWalkableArea(this); area.ID = i; _walkableAreas.Add(area); } @@ -103,7 +103,7 @@ public Room(int roomNumber) : base(roomNumber) for (int i = 0; i < MAX_REGIONS; i++) { - RoomRegion area = new RoomRegion(); + RoomRegion area = new RoomRegion(this); area.ID = i; _regions.Add(area); } @@ -114,9 +114,9 @@ public Room(XmlNode node) : base(node) _interactions.FromXml(node); _objects.AddRange(GetXmlChildren(node, "/Room/Objects", MAX_OBJECTS).Select((xml, i) => new RoomObject(this, xml) { ID = i })); _hotspots.AddRange(GetXmlChildren(node, "/Room/Hotspots", MAX_HOTSPOTS).Select((xml, i) => new RoomHotspot(this, xml) { ID = i })); - _walkableAreas.AddRange(GetXmlChildren(node, "/Room/WalkableAreas", MAX_WALKABLE_AREAS).Select((xml, i) => new RoomWalkableArea(xml) { ID = i })); + _walkableAreas.AddRange(GetXmlChildren(node, "/Room/WalkableAreas", MAX_WALKABLE_AREAS).Select((xml, i) => new RoomWalkableArea(this, xml) { ID = i })); _walkBehinds.AddRange(GetXmlChildren(node, "/Room/WalkBehinds", MAX_WALK_BEHINDS).Select((xml, i) => new RoomWalkBehind(xml) { ID = i })); - _regions.AddRange(GetXmlChildren(node, "/Room/Regions", MAX_REGIONS).Select((xml, i) => new RoomRegion(xml) { ID = i })); + _regions.AddRange(GetXmlChildren(node, "/Room/Regions", MAX_REGIONS).Select((xml, i) => new RoomRegion(this, xml) { ID = i })); } [AGSNoSerialize] diff --git a/Editor/AGS.Types/RoomRegion.cs b/Editor/AGS.Types/RoomRegion.cs index 2d0a0e3d3f..b62383190f 100644 --- a/Editor/AGS.Types/RoomRegion.cs +++ b/Editor/AGS.Types/RoomRegion.cs @@ -9,7 +9,7 @@ namespace AGS.Types { [PropertyTab(typeof(PropertyTabInteractions), PropertyTabScope.Component)] [DefaultProperty("LightLevel")] - public class RoomRegion : ICustomTypeDescriptor, IToXml + public class RoomRegion : IChangeNotification, ICustomTypeDescriptor, IToXml { private static InteractionSchema _interactionSchema; @@ -22,6 +22,8 @@ public class RoomRegion : ICustomTypeDescriptor, IToXml private int _tintAmount; private int _tintLuminance; private Interactions _interactions = new Interactions(_interactionSchema); + private CustomProperties _properties = new CustomProperties(); + private IChangeNotification _notifyOfModification; static RoomRegion() { @@ -33,11 +35,12 @@ static RoomRegion() "Region *theRegion"); } - public RoomRegion() + public RoomRegion(IChangeNotification changeNotifier) { + _notifyOfModification = changeNotifier; } - public RoomRegion(XmlNode node) : this() + public RoomRegion(IChangeNotification changeNotifier, XmlNode node) : this(changeNotifier) { SerializeUtils.DeserializeFromXML(this, node); Interactions.FromXml(node); @@ -132,6 +135,16 @@ public Interactions Interactions get { return _interactions; } } + [AGSSerializeClass()] + [Description("Custom properties for this region")] + [Category("Properties")] + [EditorAttribute(typeof(CustomPropertiesUIEditor), typeof(System.Drawing.Design.UITypeEditor))] + public CustomProperties Properties + { + get { return _properties; } + protected set { _properties = value; } + } + #region ICustomTypeDescriptor Members @@ -224,6 +237,11 @@ public object GetPropertyOwner(PropertyDescriptor pd) #endregion + void IChangeNotification.ItemModified() + { + _notifyOfModification.ItemModified(); + } + public void ToXml(XmlTextWriter writer) { SerializeUtils.SerializeToXML(this, writer, false); diff --git a/Editor/AGS.Types/RoomWalkableArea.cs b/Editor/AGS.Types/RoomWalkableArea.cs index d15b191cd8..63f1dea288 100644 --- a/Editor/AGS.Types/RoomWalkableArea.cs +++ b/Editor/AGS.Types/RoomWalkableArea.cs @@ -8,7 +8,7 @@ namespace AGS.Types { [DefaultProperty("ScalingLevel")] - public class RoomWalkableArea : ICustomTypeDescriptor, IToXml + public class RoomWalkableArea : IChangeNotification, ICustomTypeDescriptor, IToXml { private int _id; private int _areaSpecificView; @@ -17,12 +17,15 @@ public class RoomWalkableArea : ICustomTypeDescriptor, IToXml private bool _useContinuousScaling; private int _scalingLevelMin = 100; private int _scalingLevelMax = 100; + private CustomProperties _properties = new CustomProperties(); + private IChangeNotification _notifyOfModification; - public RoomWalkableArea() + public RoomWalkableArea(IChangeNotification changeNotifier) { + _notifyOfModification = changeNotifier; } - public RoomWalkableArea(XmlNode node) : this() + public RoomWalkableArea(IChangeNotification changeNotifier, XmlNode node) : this(changeNotifier) { SerializeUtils.DeserializeFromXML(this, node); } @@ -100,6 +103,16 @@ public string PropertyGridTitle get { return "Walkable area ID " + _id; } } + [AGSSerializeClass()] + [Description("Custom properties for this area")] + [Category("Properties")] + [EditorAttribute(typeof(CustomPropertiesUIEditor), typeof(System.Drawing.Design.UITypeEditor))] + public CustomProperties Properties + { + get { return _properties; } + protected set { _properties = value; } + } + #region ICustomTypeDescriptor Members @@ -189,6 +202,11 @@ public object GetPropertyOwner(PropertyDescriptor pd) #endregion + void IChangeNotification.ItemModified() + { + _notifyOfModification.ItemModified(); + } + public void ToXml(XmlTextWriter writer) => SerializeUtils.SerializeToXML(this, writer); } } diff --git a/Engine/ac/audioclip.cpp b/Engine/ac/audioclip.cpp index 66cc14dc02..bf725c2d10 100644 --- a/Engine/ac/audioclip.cpp +++ b/Engine/ac/audioclip.cpp @@ -17,6 +17,8 @@ #include "ac/common.h" // quitprintf #include "ac/game.h" #include "ac/gamesetupstruct.h" +#include "ac/gamestate.h" +#include "ac/properties.h" #include "ac/string.h" #include "ac/dynobj/cc_audioclip.h" #include "ac/dynobj/cc_audiochannel.h" @@ -94,6 +96,26 @@ ScriptAudioChannel* AudioClip_PlayOnChannel(ScriptAudioClip *clip, int chan, int return play_audio_clip_on_channel(chan, clip, priority, repeat, 0); } +int AudioClip_GetProperty(ScriptAudioClip *clip, const char *property) +{ + return get_int_property(game.audioclipProps[clip->id], play.audioclipProps[clip->id], property); +} + +const char* AudioClip_GetTextProperty(ScriptAudioClip *clip, const char *property) +{ + return get_text_property_dynamic_string(game.audioclipProps[clip->id], play.audioclipProps[clip->id], property); +} + +bool AudioClip_SetProperty(ScriptAudioClip *clip, const char *property, int value) +{ + return set_int_property(play.audioclipProps[clip->id], property, value); +} + +bool AudioClip_SetTextProperty(ScriptAudioClip *clip, const char *property, const char *value) +{ + return set_text_property(play.audioclipProps[clip->id], property, value); +} + //============================================================================= // // Script API Functions @@ -173,6 +195,26 @@ RuntimeScriptValue Sc_AudioClip_PlayOnChannel(void *self, const RuntimeScriptVal API_OBJCALL_OBJ_PINT3(ScriptAudioClip, ScriptAudioChannel, ccDynamicAudio, AudioClip_PlayOnChannel); } +RuntimeScriptValue Sc_AudioClip_GetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_INT_POBJ(ScriptAudioClip, AudioClip_GetProperty, const char); +} + +RuntimeScriptValue Sc_AudioClip_GetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_OBJ_POBJ(ScriptAudioClip, const char, myScriptStringImpl, AudioClip_GetTextProperty, const char); +} + +RuntimeScriptValue Sc_AudioClip_SetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ_PINT(ScriptAudioClip, AudioClip_SetProperty, const char); +} + +RuntimeScriptValue Sc_AudioClip_SetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ2(ScriptAudioClip, AudioClip_SetTextProperty, const char, const char); +} + void RegisterAudioClipAPI() { ScFnRegister audioclip_api[] = { @@ -182,6 +224,10 @@ void RegisterAudioClipAPI() { "AudioClip::PlayQueued^2", API_FN_PAIR(AudioClip_PlayQueued) }, { "AudioClip::PlayOnChannel^3", API_FN_PAIR(AudioClip_PlayOnChannel) }, { "AudioClip::Stop^0", API_FN_PAIR(AudioClip_Stop) }, + { "AudioClip::GetProperty^1", API_FN_PAIR(AudioClip_GetProperty) }, + { "AudioClip::GetTextProperty^1", API_FN_PAIR(AudioClip_GetTextProperty) }, + { "AudioClip::SetProperty^2", API_FN_PAIR(AudioClip_SetProperty) }, + { "AudioClip::SetTextProperty^2", API_FN_PAIR(AudioClip_SetTextProperty) }, { "AudioClip::get_ID", API_FN_PAIR(AudioClip_GetID) }, { "AudioClip::get_FileType", API_FN_PAIR(AudioClip_GetFileType) }, { "AudioClip::get_IsAvailable", API_FN_PAIR(AudioClip_GetIsAvailable) }, diff --git a/Engine/ac/dialog.cpp b/Engine/ac/dialog.cpp index 19b73113f9..5691e9b5dc 100644 --- a/Engine/ac/dialog.cpp +++ b/Engine/ac/dialog.cpp @@ -35,6 +35,7 @@ #include "ac/overlay.h" #include "ac/mouse.h" #include "ac/parser.h" +#include "ac/properties.h" #include "ac/sys_events.h" #include "ac/string.h" #include "ac/dynobj/scriptdialogoptionsrendering.h" @@ -170,6 +171,26 @@ const char *Dialog_GetScriptName(ScriptDialog *sd) return CreateNewScriptString(game.dialogScriptNames[sd->id]); } +int Dialog_GetProperty(ScriptDialog *sd, const char *property) +{ + return get_int_property(game.dialogProps[sd->id], play.dialogProps[sd->id], property); +} + +const char* Dialog_GetTextProperty(ScriptDialog *sd, const char *property) +{ + return get_text_property_dynamic_string(game.dialogProps[sd->id], play.dialogProps[sd->id], property); +} + +bool Dialog_SetProperty(ScriptDialog *sd, const char *property, int value) +{ + return set_int_property(play.dialogProps[sd->id], property, value); +} + +bool Dialog_SetTextProperty(ScriptDialog *sd, const char *property, const char *value) +{ + return set_text_property(play.dialogProps[sd->id], property, value); +} + //============================================================================= #define RUN_DIALOG_STAY -1 @@ -1403,6 +1424,26 @@ RuntimeScriptValue Sc_Dialog_Start(void *self, const RuntimeScriptValue *params, API_OBJCALL_VOID(ScriptDialog, Dialog_Start); } +RuntimeScriptValue Sc_Dialog_GetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_INT_POBJ(ScriptDialog, Dialog_GetProperty, const char); +} + +RuntimeScriptValue Sc_Dialog_GetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_OBJ_POBJ(ScriptDialog, const char, myScriptStringImpl, Dialog_GetTextProperty, const char); +} + +RuntimeScriptValue Sc_Dialog_SetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ_PINT(ScriptDialog, Dialog_SetProperty, const char); +} + +RuntimeScriptValue Sc_Dialog_SetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ2(ScriptDialog, Dialog_SetTextProperty, const char, const char); +} + void RegisterDialogAPI() { ScFnRegister dialog_api[] = { @@ -1421,6 +1462,10 @@ void RegisterDialogAPI() { "Dialog::SetHasOptionBeenChosen^2", API_FN_PAIR(Dialog_SetHasOptionBeenChosen) }, { "Dialog::SetOptionState^2", API_FN_PAIR(Dialog_SetOptionState) }, { "Dialog::Start^0", API_FN_PAIR(Dialog_Start) }, + { "Dialog::GetProperty^1", API_FN_PAIR(Dialog_GetProperty) }, + { "Dialog::GetTextProperty^1", API_FN_PAIR(Dialog_GetTextProperty) }, + { "Dialog::SetProperty^2", API_FN_PAIR(Dialog_SetProperty) }, + { "Dialog::SetTextProperty^2", API_FN_PAIR(Dialog_SetTextProperty) }, }; ccAddExternalFunctions(dialog_api); diff --git a/Engine/ac/gamestate.cpp b/Engine/ac/gamestate.cpp index 1447fc3a9a..3070d5ef7f 100644 --- a/Engine/ac/gamestate.cpp +++ b/Engine/ac/gamestate.cpp @@ -839,8 +839,14 @@ void GamePlayState::WriteForSavegame(Stream *out) const void GamePlayState::FreeProperties() { + for (auto &p : audioclipProps) + p.clear(); for (auto &p : charProps) p.clear(); + for (auto &p : dialogProps) + p.clear(); + for (auto &p : guiProps) + p.clear(); for (auto &p : invProps) p.clear(); } diff --git a/Engine/ac/gamestate.h b/Engine/ac/gamestate.h index 5f21de419e..4a6931384c 100644 --- a/Engine/ac/gamestate.h +++ b/Engine/ac/gamestate.h @@ -246,9 +246,14 @@ struct GamePlayState int default_audio_type_volumes[MAX_AUDIO_TYPES]{}; float face_dir_ratio = 1.f; // character face direction ratio, defines y/x relation - // Dynamic custom property values for characters and items + // Dynamic custom property values for global game objects std::vector charProps; - AGS::Common::StringIMap invProps[MAX_INV]; + std::vector dialogProps; + std::vector guiProps; + AGS::Common::StringIMap invProps[MAX_INV]; + // NOTE: audioclip custom properties are not written into game saves; + // this is done on purpose, as audio clips are resources and not a part of a game state. + std::vector audioclipProps; // Dynamic speech state // diff --git a/Engine/ac/gui.cpp b/Engine/ac/gui.cpp index 31066f5318..81dc695ddf 100644 --- a/Engine/ac/gui.cpp +++ b/Engine/ac/gui.cpp @@ -30,6 +30,7 @@ #include "ac/guicontrol.h" #include "ac/invwindow.h" #include "ac/mouse.h" +#include "ac/properties.h" #include "ac/runtime_defines.h" #include "ac/string.h" #include "ac/system.h" @@ -449,6 +450,26 @@ void GUI_SetScale(ScriptGUI *gui, float scalex, float scaley) { guis[gui->id].SetScale(scalex, scaley); } +int GUI_GetProperty(ScriptGUI *gui, const char *property) +{ + return get_int_property(game.guiProps[gui->id], play.guiProps[gui->id], property); +} + +const char* GUI_GetTextProperty(ScriptGUI *gui, const char *property) +{ + return get_text_property_dynamic_string(game.guiProps[gui->id], play.guiProps[gui->id], property); +} + +bool GUI_SetProperty(ScriptGUI *gui, const char *property, int value) +{ + return set_int_property(play.guiProps[gui->id], property, value); +} + +bool GUI_SetTextProperty(ScriptGUI *gui, const char *property, const char *value) +{ + return set_text_property(play.guiProps[gui->id], property, value); +} + //============================================================================= void remove_popup_interface(int ifacenum) { @@ -1168,6 +1189,26 @@ RuntimeScriptValue Sc_GUI_GUIToScreenPoint(void *self, const RuntimeScriptValue API_OBJCALL_OBJAUTO_PINT2_PBOOL(ScriptGUI, ScriptUserObject, GUI_GUIToScreenPoint); } +RuntimeScriptValue Sc_GUI_GetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_INT_POBJ(ScriptGUI, GUI_GetProperty, const char); +} + +RuntimeScriptValue Sc_GUI_GetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_OBJ_POBJ(ScriptGUI, const char, myScriptStringImpl, GUI_GetTextProperty, const char); +} + +RuntimeScriptValue Sc_GUI_SetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ_PINT(ScriptGUI, GUI_SetProperty, const char); +} + +RuntimeScriptValue Sc_GUI_SetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ2(ScriptGUI, GUI_SetTextProperty, const char, const char); +} + void RegisterGUIAPI() { ScFnRegister gui_api[] = { @@ -1180,6 +1221,10 @@ void RegisterGUIAPI() { "GUI::Click^1", API_FN_PAIR(GUI_Click) }, { "GUI::SetPosition^2", API_FN_PAIR(GUI_SetPosition) }, { "GUI::SetSize^2", API_FN_PAIR(GUI_SetSize) }, + { "GUI::GetProperty^1", API_FN_PAIR(GUI_GetProperty) }, + { "GUI::GetTextProperty^1", API_FN_PAIR(GUI_GetTextProperty) }, + { "GUI::SetProperty^2", API_FN_PAIR(GUI_SetProperty) }, + { "GUI::SetTextProperty^2", API_FN_PAIR(GUI_SetTextProperty) }, { "GUI::get_BackgroundGraphic", API_FN_PAIR(GUI_GetBackgroundGraphic) }, { "GUI::set_BackgroundGraphic", API_FN_PAIR(GUI_SetBackgroundGraphic) }, { "GUI::get_BackgroundColor", API_FN_PAIR(GUI_GetBackgroundColor) }, diff --git a/Engine/ac/hotspot.cpp b/Engine/ac/hotspot.cpp index 6982f98034..e7aa6fc9e0 100644 --- a/Engine/ac/hotspot.cpp +++ b/Engine/ac/hotspot.cpp @@ -114,15 +114,14 @@ void Hotspot_RunInteraction (ScriptHotspot *hss, int mood) { RunHotspotInteraction(hss->id, mood); } -int Hotspot_GetProperty (ScriptHotspot *hss, const char *property) +int Hotspot_GetProperty(ScriptHotspot *hss, const char *property) { return get_int_property(thisroom.Hotspots[hss->id].Properties, croom->hsProps[hss->id], property); } -void Hotspot_GetPropertyText (ScriptHotspot *hss, const char *property, char *bufer) +void Hotspot_GetPropertyText(ScriptHotspot *hss, const char *property, char *bufer) { get_text_property(thisroom.Hotspots[hss->id].Properties, croom->hsProps[hss->id], property, bufer); - } const char* Hotspot_GetTextProperty(ScriptHotspot *hss, const char *property) diff --git a/Engine/ac/region.cpp b/Engine/ac/region.cpp index 8a5aa528cb..57af00e8dc 100644 --- a/Engine/ac/region.cpp +++ b/Engine/ac/region.cpp @@ -17,10 +17,12 @@ #include "ac/gamesetupstruct.h" #include "ac/gamestate.h" #include "ac/global_region.h" +#include "ac/properties.h" #include "ac/room.h" #include "ac/roomstatus.h" #include "ac/dynobj/cc_region.h" #include "ac/dynobj/scriptdrawingsurface.h" +#include "ac/dynobj/scriptstring.h" #include "game/roomstruct.h" #include "script/runtimescriptvalue.h" @@ -116,6 +118,26 @@ void Region_RunInteraction(ScriptRegion *ssr, int mood) { RunRegionInteraction(ssr->id, mood); } +int Region_GetProperty(ScriptRegion *ssr, const char *property) +{ + return get_int_property(thisroom.Regions[ssr->id].Properties, croom->regProps[ssr->id], property); +} + +const char* Region_GetTextProperty(ScriptRegion *ssr, const char *property) +{ + return get_text_property_dynamic_string(thisroom.Regions[ssr->id].Properties, croom->regProps[ssr->id], property); +} + +bool Region_SetProperty(ScriptRegion *ssr, const char *property, int value) +{ + return set_int_property(croom->regProps[ssr->id], property, value); +} + +bool Region_SetTextProperty(ScriptRegion *ssr, const char *property, const char *value) +{ + return set_text_property(croom->regProps[ssr->id], property, value); +} + //============================================================================= void generate_light_table() @@ -234,6 +256,26 @@ RuntimeScriptValue Sc_Region_GetTintLuminance(void *self, const RuntimeScriptVal API_OBJCALL_INT(ScriptRegion, Region_GetTintLuminance); } +RuntimeScriptValue Sc_Region_GetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_INT_POBJ(ScriptRegion, Region_GetProperty, const char); +} + +RuntimeScriptValue Sc_Region_GetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_OBJ_POBJ(ScriptRegion, const char, myScriptStringImpl, Region_GetTextProperty, const char); +} + +RuntimeScriptValue Sc_Region_SetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ_PINT(ScriptRegion, Region_SetProperty, const char); +} + +RuntimeScriptValue Sc_Region_SetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ2(ScriptRegion, Region_SetTextProperty, const char, const char); +} + void RegisterRegionAPI() @@ -246,6 +288,10 @@ void RegisterRegionAPI() { "Region::Tint^4", API_FN_PAIR(Region_TintNoLum) }, { "Region::Tint^5", API_FN_PAIR(Region_Tint) }, { "Region::RunInteraction^1", API_FN_PAIR(Region_RunInteraction) }, + { "Region::GetProperty^1", API_FN_PAIR(Region_GetProperty) }, + { "Region::GetTextProperty^1", API_FN_PAIR(Region_GetTextProperty) }, + { "Region::SetProperty^2", API_FN_PAIR(Region_SetProperty) }, + { "Region::SetTextProperty^2", API_FN_PAIR(Region_SetTextProperty) }, { "Region::get_Enabled", API_FN_PAIR(Region_GetEnabled) }, { "Region::set_Enabled", API_FN_PAIR(Region_SetEnabled) }, { "Region::get_ID", API_FN_PAIR(Region_GetID) }, diff --git a/Engine/ac/room.cpp b/Engine/ac/room.cpp index f4262bddb3..837a29d6d7 100644 --- a/Engine/ac/room.cpp +++ b/Engine/ac/room.cpp @@ -530,10 +530,7 @@ void load_new_room(int newnum, CharacterInfo*forchar) { else croom=&troom; // Decide what to do if we have been or not in this room before - if (croom->beenhere > 0) - { - } - else + if (croom->beenhere == 0) { // If we have not been in this room before, then copy necessary fields from thisroom croom->numobj=thisroom.Objects.size(); diff --git a/Engine/ac/roomstatus.cpp b/Engine/ac/roomstatus.cpp index 025c52d6db..ac610c8ed5 100644 --- a/Engine/ac/roomstatus.cpp +++ b/Engine/ac/roomstatus.cpp @@ -89,6 +89,14 @@ void RoomStatus::FreeProperties() hsProps[i].clear(); } objProps.clear(); + for (int i = 0; i < MAX_ROOM_REGIONS; ++i) + { + regProps[i].clear(); + } + for (int i = 0; i < MAX_WALK_AREAS; ++i) + { + waProps[i].clear(); + } } void RoomStatus::ReadFromSavegame(Stream *in, RoomStatSvgVersion cmp_ver) @@ -164,6 +172,17 @@ void RoomStatus::ReadFromSavegame(Stream *in, RoomStatSvgVersion cmp_ver) in->ReadInt32(); in->ReadInt32(); } + if (cmp_ver >= kRoomStatSvgVersion_40008) + { + for (int i = 0; i < num_regions; ++i) + { + Properties::ReadValues(regProps[i], in); + } + for (int i = 0; i < num_walkareas; ++i) + { + Properties::ReadValues(waProps[i], in); + } + } } void RoomStatus::WriteToSavegame(Stream *out) const @@ -219,6 +238,16 @@ void RoomStatus::WriteToSavegame(Stream *out) const out->WriteInt32(0); out->WriteInt32(0); out->WriteInt32(0); + + // -- kRoomStatSvgVersion_40008 + for (int i = 0; i < MAX_ROOM_REGIONS; ++i) + { + Properties::WriteValues(regProps[i], out); + } + for (int i = 0; i < MAX_WALK_AREAS; ++i) + { + Properties::WriteValues(waProps[i], out); + } } std::unique_ptr room_statuses[MAX_ROOMS]; diff --git a/Engine/ac/roomstatus.h b/Engine/ac/roomstatus.h index 9a464f0b8f..1478162ef2 100644 --- a/Engine/ac/roomstatus.h +++ b/Engine/ac/roomstatus.h @@ -37,6 +37,7 @@ enum RoomStatSvgVersion kRoomStatSvgVersion_36109 = 5, // removed movelists, save externally kRoomStatSvgVersion_400 = 4000000, // room object blendmodes etc kRoomStatSvgVersion_40003 = 4000003, // room object flags as 32-bit, facedirratio + kRoomStatSvgVersion_40008 = 4000008, // custom properties for regions and walk-areas kRoomStatSvgVersion_Current = kRoomStatSvgVersion_40003 }; @@ -66,15 +67,18 @@ struct RoomStatus uint32_t tsdatasize; std::vector tsdata; - Common::StringIMap roomProps; - Common::StringIMap hsProps[MAX_ROOM_HOTSPOTS]; - std::vector objProps; HotspotState hotspot[MAX_ROOM_HOTSPOTS]; char region_enabled[MAX_ROOM_REGIONS]; short walkbehind_base[MAX_WALK_BEHINDS]; float face_dir_ratio = 0.f; WalkareaState walkareas[MAX_WALK_AREAS]; + Common::StringIMap roomProps; + std::vector objProps; + Common::StringIMap hsProps[MAX_ROOM_HOTSPOTS]; + Common::StringIMap regProps[MAX_ROOM_REGIONS]; + Common::StringIMap waProps[MAX_WALK_AREAS]; + // A version of a save this RoomStatus was restored from. // This is used as a hint when merging RoomStatus with the loaded room file (upon room enter). // We need this for cases when an old format save is restored within an upgraded game diff --git a/Engine/ac/walkablearea.cpp b/Engine/ac/walkablearea.cpp index 4b42c8f35c..320ac40416 100644 --- a/Engine/ac/walkablearea.cpp +++ b/Engine/ac/walkablearea.cpp @@ -18,6 +18,7 @@ #include "ac/gamesetupstruct.h" #include "ac/global_walkablearea.h" #include "ac/object.h" +#include "ac/properties.h" #include "ac/room.h" #include "ac/roomobject.h" #include "ac/roomstatus.h" @@ -287,6 +288,26 @@ void Walkarea_SetFaceDirectionRatio(ScriptWalkableArea *wa, float ratio) thisroom.WalkAreas[wa->id].FaceDirectionRatio = ratio; } +int Walkarea_GetProperty(ScriptWalkableArea *wa, const char *property) +{ + return get_int_property(thisroom.WalkAreas[wa->id].Properties, croom->waProps[wa->id], property); +} + +const char* Walkarea_GetTextProperty(ScriptWalkableArea *wa, const char *property) +{ + return get_text_property_dynamic_string(thisroom.WalkAreas[wa->id].Properties, croom->waProps[wa->id], property); +} + +bool Walkarea_SetProperty(ScriptWalkableArea *wa, const char *property, int value) +{ + return set_int_property(croom->waProps[wa->id], property, value); +} + +bool Walkarea_SetTextProperty(ScriptWalkableArea *wa, const char *property, const char *value) +{ + return set_text_property(croom->waProps[wa->id], property, value); +} + //============================================================================= // // Script API Functions @@ -356,6 +377,26 @@ RuntimeScriptValue Sc_Walkarea_SetFaceDirectionRatio(void *self, const RuntimeSc API_OBJCALL_VOID_PFLOAT(ScriptWalkableArea, Walkarea_SetFaceDirectionRatio); } +RuntimeScriptValue Sc_Walkarea_GetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_INT_POBJ(ScriptWalkableArea, Walkarea_GetProperty, const char); +} + +RuntimeScriptValue Sc_Walkarea_GetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_OBJ_POBJ(ScriptWalkableArea, const char, myScriptStringImpl, Walkarea_GetTextProperty, const char); +} + +RuntimeScriptValue Sc_Walkarea_SetProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ_PINT(ScriptWalkableArea, Walkarea_SetProperty, const char); +} + +RuntimeScriptValue Sc_Walkarea_SetTextProperty(void *self, const RuntimeScriptValue *params, int32_t param_count) +{ + API_OBJCALL_BOOL_POBJ2(ScriptWalkableArea, Walkarea_SetTextProperty, const char, const char); +} + void RegisterWalkareaAPI() { @@ -365,6 +406,10 @@ void RegisterWalkareaAPI() { "WalkableArea::GetDrawingSurface", API_FN_PAIR(GetDrawingSurfaceForWalkableArea) }, { "WalkableArea::SetScaling^2", API_FN_PAIR(Walkarea_SetScaling) }, + { "WalkableArea::GetProperty^1", API_FN_PAIR(Walkarea_GetProperty) }, + { "WalkableArea::GetTextProperty^1", API_FN_PAIR(Walkarea_GetTextProperty) }, + { "WalkableArea::SetProperty^2", API_FN_PAIR(Walkarea_SetProperty) }, + { "WalkableArea::SetTextProperty^2", API_FN_PAIR(Walkarea_SetTextProperty) }, { "WalkableArea::get_Enabled", API_FN_PAIR(Walkarea_GetEnabled) }, { "WalkableArea::set_Enabled", API_FN_PAIR(Walkarea_SetEnabled) }, { "WalkableArea::get_ID", API_FN_PAIR(Walkarea_GetID) }, diff --git a/Engine/game/game_init.cpp b/Engine/game/game_init.cpp index 23256dde2d..9721e8bf45 100644 --- a/Engine/game/game_init.cpp +++ b/Engine/game/game_init.cpp @@ -448,7 +448,10 @@ HGameInitError InitGameState(const LoadedGameEntities &ents, GameDataVersion dat GUIRefCollection guictrl_refs(guibuts, guiinv, guilabels, guilist, guislider, guitext); GUI::RebuildGUI(guis, guictrl_refs); views = std::move(ents.Views); + play.audioclipProps.resize(game.audioClips.size()); play.charProps.resize(game.numcharacters); + play.dialogProps.resize(game.numdialog); + play.guiProps.resize(game.numgui); dialog = std::move(ents.Dialogs); // Set number of game channels corresponding to the loaded game version game.numGameChannels = MAX_GAME_CHANNELS; diff --git a/Engine/game/savegame_components.cpp b/Engine/game/savegame_components.cpp index c8b16093fd..b835553917 100644 --- a/Engine/game/savegame_components.cpp +++ b/Engine/game/savegame_components.cpp @@ -318,6 +318,14 @@ HSaveError ReadGameState(Stream *in, int32_t cmp_ver, soff_t cmp_size, const Pre return err; } +// Savegame data format for Audio system +enum AudioSvgVersion +{ + kAudioSvgVersion_Initial = 0, + kAudioSvgVersion_35026 = 1, // source position settings + kAudioSvgVersion_36009 = 2, // up number of channels +}; + HSaveError WriteAudio(Stream *out) { // Game content assertion @@ -377,14 +385,6 @@ HSaveError WriteAudio(Stream *out) return HSaveError::None(); } -// Savegame data format for RoomStatus -enum AudioSvgVersion -{ - kAudioSvgVersion_Initial = 0, - kAudioSvgVersion_35026 = 1, // source position settings - kAudioSvgVersion_36009 = 2, // up number of channels -}; - HSaveError ReadAudio(Stream *in, int32_t cmp_ver, soff_t cmp_size, const PreservedParams& /*pp*/, RestoredData &r_data) { HSaveError err; @@ -492,18 +492,22 @@ HSaveError WriteDialogs(Stream *out) for (int i = 0; i < game.numdialog; ++i) { dialog[i].WriteToSavegame(out); + Properties::WriteValues(play.dialogProps[i], out); } return HSaveError::None(); } -HSaveError ReadDialogs(Stream *in, int32_t /*cmp_ver*/, soff_t cmp_size, const PreservedParams& /*pp*/, RestoredData& /*r_data*/) +HSaveError ReadDialogs(Stream *in, int32_t cmp_ver, soff_t cmp_size, const PreservedParams& /*pp*/, RestoredData& /*r_data*/) { HSaveError err; if (!AssertGameContent(err, in->ReadInt32(), game.numdialog, "Dialogs")) return err; + const DialogSvgVersion svg_ver = (DialogSvgVersion)cmp_ver; for (int i = 0; i < game.numdialog; ++i) { - dialog[i].ReadFromSavegame(in); + dialog[i].ReadFromSavegame(in, svg_ver); + if (cmp_ver >= kDialogSvgVersion_40008) + Properties::ReadValues(play.dialogProps[i], in); } return err; } @@ -513,8 +517,11 @@ HSaveError WriteGUI(Stream *out) // GUI state WriteFormatTag(out, "GUIs"); out->WriteInt32(game.numgui); - for (const auto &gui : guis) - gui.WriteToSavegame(out); + for (int i = 0; i < game.numgui; ++i) + { + guis[i].WriteToSavegame(out); + Properties::WriteValues(play.guiProps[i], out); + } WriteFormatTag(out, "GUIButtons"); out->WriteInt32(static_cast(guibuts.size())); @@ -565,7 +572,11 @@ HSaveError ReadGUI(Stream *in, int32_t cmp_ver, soff_t cmp_size, const Preserved if (!AssertGameContent(err, static_cast(in->ReadInt32()), game.numgui, "GUIs")) return err; for (int i = 0; i < game.numgui; ++i) + { guis[i].ReadFromSavegame(in, svg_ver); + if (svg_ver >= kGuiSvgVersion_40008) + Properties::ReadValues(play.guiProps[i], in); + } if (!AssertFormatTagStrict(err, in, "GUIButtons")) return err; @@ -1181,14 +1192,14 @@ ComponentHandler ComponentHandlers[] = }, { "Dialogs", - 0, - 0, + kDialogSvgVersion_40008, + kDialogSvgVersion_Initial, WriteDialogs, ReadDialogs }, { "GUI", - kGuiSvgVersion_400, + kGuiSvgVersion_40008, kGuiSvgVersion_Initial, WriteGUI, ReadGUI @@ -1244,14 +1255,14 @@ ComponentHandler ComponentHandlers[] = }, { "Room States", - kRoomStatSvgVersion_40003, + kRoomStatSvgVersion_40008, kRoomStatSvgVersion_40003, WriteRoomStates, ReadRoomStates }, { "Loaded Room State", - kRoomStatSvgVersion_40003, // must correspond to "Room States" + kRoomStatSvgVersion_40008, // must correspond to "Room States" kRoomStatSvgVersion_40003, WriteThisRoom, ReadThisRoom