Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate MCMBuilder into MCM #56

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Next Next commit
Adds MCMBuilderClientTestMod project
Musashi1584 committed May 26, 2020
commit aabe2a59190967d91a9b1fd601e117661b564d9b
Binary file added MCMBuilderClientTestMod/InterfacesPackage.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions MCMBuilderClientTestMod/MCMBuilderClientTestMod.XCOM_sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# XCOM ModBuddy Solution File, Format Version 11.00
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{5DAE07AF-E217-45C1-8DE7-FF99D6011E8A}") = "MCMBuilderClientTestMod", "MCMBuilderClientTestMod\MCMBuilderClientTestMod.x2proj", "{E97347C9-10D6-4B92-BFEB-B7E2BFDA1983}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Default|XCOM 2 = Default|XCOM 2
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E97347C9-10D6-4B92-BFEB-B7E2BFDA1983}.Default|XCOM 2.ActiveCfg = Default|XCOM 2
{E97347C9-10D6-4B92-BFEB-B7E2BFDA1983}.Default|XCOM 2.Build.0 = Default|XCOM 2
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[ModPackages]
+ModPackages=MCMBuilderClientTestMod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[Engine.ScriptPackages]
+NonNativePackages=ModConfigMenuBuilderAPI_1_0_0
+NonNativePackages=MCMBuilderClientTestMod

[UnrealEd.EditorEngine]
+ModEditPackages=ModConfigMenuBuilderAPI_1_0_0
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[MCMBuilderClientTestMod.X2DownloadableContentInfo_MCMBuilderClientTestMod]
DLCIdentifier="MCMBuilderClientTestMod"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[MCMBuilderClientTestModConfigManager JsonConfig_ManagerDefault]
+ConfigProperties = {"HUNGRY":{"Value":"true"}}
+ConfigProperties = {"HUNGER_SCALE":{"Value":"1"}}
+ConfigProperties = {"HUNGER_SCALE_NERD":{"Value":"0.1"}}
+ConfigProperties = {"FOOD":{"Value":"Apple"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[ModConfigMenuBuilder.MCM_Builder_Screen]
+MCMBuilder="MCMBuilderClientTestMod"

[MCMBuilderClientTestMod JsonConfig_MCM_Builder]
+MCMPages = { \\
"TESTMOD_SETTINGS_PAGE":{ \\
"SaveConfigManager": "MCMBuilderClientTestModConfigManager",\\
"EnableResetButton": "true", \\
"TESTMOD_SETTINGS_GROUP_1":{ \\
"HUNGRY": { "Type": "Checkbox" }, \\
"HUNGER_SCALE_NERD": { "Type": "Slider", "SliderMin": "0.0", "SliderMax": "1.0", "SliderStep":"0.1" }, \\
"HUNGER_SCALE": { "Type": "Spinner", "Options": "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" }, \\
"FOOD": { "Type": "Dropdown", "Options": "Apple, Chocolate, Burger" }, \\
}, \\
"TESTMOD_SETTINGS_GROUP_2":{ \\
"A_LABEL": { "Type": "Label" }, \\
}, \\
}, \\
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Default" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Guid>91050dba-9f3c-4243-90d9-f6bdef9ad716</Guid>
<Name>MCMBuilderClientTestMod</Name>
<Description>Description of My XCOM 2 Mod.</Description>
<SteamPublishID>0</SteamPublishID>
<AssemblyName>MCMBuilderClientTestMod</AssemblyName>
<RootNamespace>MCMBuilderClientTestMod</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Folder Include="Config\" />
<Folder Include="Localization\" />
<Folder Include="Src\" />
<Folder Include="Src\MCMBuilderClientTestMod\" />
<Folder Include="Src\MCMBuilderClientTestMod\Classes\" />
<Folder Include="Src\ModConfigMenuBuilderAPI_1_0_0" />
<Folder Include="Src\ModConfigMenuBuilderAPI_1_0_0\Classes" />
</ItemGroup>
<ItemGroup>
<Content Include="Config\XComJsonConfigManager.ini">
<SubType>Content</SubType>
</Content>
<Content Include="Config\XComMCMBuilder.ini">
<SubType>Content</SubType>
</Content>
<Content Include="Localization\ModConfigMenu.int">
<SubType>Content</SubType>
</Content>
<Content Include="ReadMe.txt" />
<Content Include="ModPreview.jpg" />
<Content Include="Config\XComEditor.ini" />
<Content Include="Config\XComEngine.ini" />
<Content Include="Config\XComGame.ini" />
<Content Include="Src\MCMBuilderClientTestMod\Classes\X2DownloadableContentInfo_MCMBuilderClientTestMod.uc" />
<Content Include="Src\MCMBuilderClientTestMod\Classes\X2EventListener_TestMod.uc">
<SubType>Content</SubType>
</Content>
<Content Include="Src\ModConfigMenuBuilderAPI_1_0_0\Classes\ConfigFactory.uc">
<SubType>Content</SubType>
</Content>
<Content Include="Src\ModConfigMenuBuilderAPI_1_0_0\Classes\JsonConfig_ManagerInterface.uc">
<SubType>Content</SubType>
</Content>
<Content Include="Src\ModConfigMenuBuilderAPI_1_0_0\Classes\MCM_Builder_SingletonFactoryInterface.uc">
<SubType>Content</SubType>
</Content>
</ItemGroup>
<Import Project="$(MSBuildLocalExtensionPath)\XCOM2.targets" />
</Project>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions MCMBuilderClientTestMod/MCMBuilderClientTestMod/ReadMe.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
You created an XCOM 2 Mod Project!
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//---------------------------------------------------------------------------------------
// FILE: XComDownloadableContentInfo_MCMBuilderClientTestMod.uc
//
// Use the X2DownloadableContentInfo class to specify unique mod behavior when the
// player creates a new campaign or loads a saved game.
//
//---------------------------------------------------------------------------------------
// Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------

class X2DownloadableContentInfo_MCMBuilderClientTestMod extends X2DownloadableContentInfo;

static function bool AbilityTagExpandHandler(string InString, out string OutString)
{
local JsonConfig_ManagerInterface ConfigManager;
local string PossibleValue;

ConfigManager = class'ConfigFactory'.static.GetConfigManager("MCMBuilderClientTestModConfigManager");

PossibleValue = ConfigManager.GetConfigTagValue(InString);
if (PossibleValue != "")
{
OutString = PossibleValue;
return true;
}

return false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
class X2EventListener_TestMod extends X2EventListener;

static function array<X2DataTemplate> CreateTemplates()
{
local array<X2DataTemplate> Templates;

Templates.AddItem(CreateListenerTemplate_MCMBuilderListener());

return Templates;
}

static function CHEventListenerTemplate CreateListenerTemplate_MCMBuilderListener()
{
local CHEventListenerTemplate Template;

`CREATE_X2TEMPLATE(class'CHEventListenerTemplate', Template, 'MCMBuilderClientTestMod_MCMBuilderListener');

Template.RegisterInStrategy = true;
Template.RegisterInTactical = true;

Template.AddCHEvent('MCM_ButtonClick', OnMCM_ButtonClick, ELD_Immediate);
Template.AddCHEvent('MCM_ChangeHandler', OnMCM_ChangeHandler, ELD_Immediate);
Template.AddCHEvent('MCM_SaveHandler', OnMCM_SaveHandler, ELD_Immediate);
Template.AddCHEvent('MCM_SaveButtonClicked', OnMCM_SaveButtonClicked, ELD_Immediate);
Template.AddCHEvent('MCM_ConfigSaved', OnMCM_ConfigSaved, ELD_Immediate);
Template.AddCHEvent('MCM_ResetButtonClicked', OnMCM_ResetButtonClicked, ELD_Immediate);
Template.AddCHEvent('MCM_ConfigResetted', OnMCM_ConfigResetted, ELD_Immediate);

return Template;
}


static function EventListenerReturn OnMCM_ButtonClick(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
if (EventSource != none)
{
`LOG(default.class @ GetFuncName() @ EventSource,, 'MCMBuilderClientTestMod');
}

return ELR_NoInterrupt;
}

static function EventListenerReturn OnMCM_ChangeHandler(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
local XComLWTuple Tuple;

Tuple = XComLWTuple(EventSource);

if (Tuple.Data[0].o != none)
{
`LOG(default.class @ GetFuncName() @ Tuple.Data[0].o @ Tuple.Data[1].s,, 'MCMBuilderClientTestMod');
}

return ELR_NoInterrupt;
}

static function EventListenerReturn OnMCM_SaveHandler(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
local XComLWTuple Tuple;

Tuple = XComLWTuple(EventSource);

if (Tuple.Data[0].o != none)
{
`LOG(default.class @ GetFuncName() @ Tuple.Data[0].o @ Tuple.Data[1].s,, 'MCMBuilderClientTestMod');
}

return ELR_NoInterrupt;
}

static function EventListenerReturn OnMCM_SaveButtonClicked(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
local XComLWTuple Tuple;

Tuple = XComLWTuple(EventSource);

if (Tuple.Data[0].o != none)
{
`LOG(default.class @ Tuple.Data[0].o @ GetFuncName(),, 'MCMBuilderClientTestMod');
}

return ELR_NoInterrupt;
}

static function EventListenerReturn OnMCM_ConfigSaved(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
if (EventSource != none)
{
`LOG(default.class @ GetFuncName() @ EventSource,, 'MCMBuilderClientTestMod');
MakePopup();
}

return ELR_NoInterrupt;
}

static function EventListenerReturn OnMCM_ResetButtonClicked(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
if (EventSource != none)
{
`LOG(default.class @ GetFuncName() @ EventSource,, 'MCMBuilderClientTestMod');
}

return ELR_NoInterrupt;
}

static function EventListenerReturn OnMCM_ConfigResetted(Object EventData, Object EventSource, XComGameState GameState, Name Event, Object CallbackData)
{
if (EventSource != none)
{
`LOG(default.class @ GetFuncName() @ EventSource,, 'MCMBuilderClientTestMod');
}

return ELR_NoInterrupt;
}

simulated static function MakePopup()
{
local TDialogueBoxData kDialogData;
local JsonConfig_ManagerInterface ConfigManager;

ConfigManager = class'ConfigFactory'.static.GetConfigManager("MCMBuilderClientTestModConfigManager");

kDialogData.eType = eDialog_Warning;
kDialogData.strTitle = "MCM Settings Saved";
kDialogData.strText =
ConfigManager.GetConfigBoolValue("A_BOOL_PROPERTY") $ "\n" $
ConfigManager.GetConfigIntValue("A_INT_PROPERTY") $ "\n" $
ConfigManager.GetConfigFloatValue("A_FLOAT_PROPERTY") $ "\n" $
ConfigManager.GetConfigStringValue("A_STRING_PROPERTY");
//kDialogData.fnCallback = OKClickedGeneric;

kDialogData.strAccept = class'UIUtilities_Text'.default.m_strGenericContinue;

`PRESBASE.UIRaiseDialog(kDialogData);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//-----------------------------------------------------------
// Class: ConfigFactory
// Author: Musashi
// DO NOT MAKE ANY CHANGES TO THIS CLASS
//-----------------------------------------------------------
class ConfigFactory extends Object;

static function JsonConfig_ManagerInterface GetConfigManager(string ManagerName)
{
local MCM_Builder_SingletonFactoryInterface SingletonFactoryInterface;
local object SingletonFactoryCDO;

SingletonFactoryCDO = class'XComEngine'.static.GetClassDefaultObjectByName('MCM_Builder_SingletonFactory');
SingletonFactoryInterface = MCM_Builder_SingletonFactoryInterface(SingletonFactoryCDO);
return SingletonFactoryInterface.static.GetManagerInstance(ManagerName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//-----------------------------------------------------------
// Class: JsonConfig_ManagerInterface
// Author: Musashi
// DO NOT MAKE ANY CHANGES TO THIS CLASS
//-----------------------------------------------------------
interface JsonConfig_ManagerInterface;

static public function JsonConfig_ManagerInterface GetConfigManager(string InstanceName, optional bool bHasDefaultConfig = true);

static public function JsonConfig_ManagerInterface GetDefaultConfigManager();

public function SerializeAndSaveConfig();

public function bool HasConfigProperty(coerce string PropertyName, optional string Namespace);

public function SetConfigString(string PropertyName, coerce string Value);

public function int GetConfigIntValue(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function float GetConfigFloatValue(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function name GetConfigNameValue(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function int GetConfigByteValue(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function bool GetConfigBoolValue(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function array<int> GetConfigIntArray(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function array<float> GetConfigFloatArray(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function array<name> GetConfigNameArray(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function vector GetConfigVectorValue(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function array<string> GetConfigStringArray(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function WeaponDamageValue GetConfigDamageValue(coerce string PropertyName, optional string Namespace);

public function string GetConfigStringValue(coerce string PropertyName, optional string TagFunction, optional string Namespace);

public function string GetConfigTagValue(coerce string PropertyName, optional string Namespace);

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//-----------------------------------------------------------
// Class: MCM_Builder_SingletonFactoryInterface
// Author: Musashi
// DO NOT MAKE ANY CHANGES TO THIS CLASS
//-----------------------------------------------------------

interface MCM_Builder_SingletonFactoryInterface;

static function JsonConfig_ManagerInterface GetManagerInstance(string InstanceName, optional bool bHasDefaultConfig = true);
173 changes: 173 additions & 0 deletions MCMBuilderClientTestMod/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# MCMBuilderClientTestMod
## MCM Builder

MCM Builder provides a new way of creating MCM Pages.
Instead of writing the mcm code the pages are completely generated from a json config.
Lets get right down to it and have a look at this example config:

```
[MusashisModToolbox.MCM_Builder_Screen]
+MCMBuilder="MCMBuilderClientTestMod"
[MCMBuilderClientTestMod JsonConfig_MCM_Builder]
+MCMPages = { \\
"TESTMOD_SETTINGS_PAGE":{ \\
"SaveConfigManager": "MCMBuilderClientTestModConfigManager",\\
"EnableResetButton": "true", \\
"TESTMOD_SETTINGS_GROUP_1":{ \\
"HUNGRY": { "Type": "Checkbox" }, \\
"HUNGER_SCALE_NERD": { "Type": "Slider", "SliderMin": "0.0", "SliderMax": "1.0", "SliderStep":"0.1" }, \\
"HUNGER_SCALE": { "Type": "Spinner", "Options": "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" }, \\
"FOOD": { "Type": "Dropdown", "Options": "Apple, Chocolate, Burger" }, \\
}, \\
"TESTMOD_SETTINGS_GROUP_2":{ \\
"A_LABEL": { "Type": "Label" }, \\
}, \\
}, \\
}
```

This small config is enough to generate a complete MCM page.
Lets have a look at the elements in detail.

### Pages and Groups

```
TESTMOD_SETTINGS_PAGE
TESTMOD_SETTINGS_GROUP_1
TESTMOD_SETTINGS_GROUP_2
```
are mcm page or group identifiers that are used to for localization mapping:
`MusashisModToolbox.int`
```
[MCMBuilderClientTestMod JsonConfig_MCM_Builder]
TESTMOD_SETTINGS_PAGE_TITLE="Test Mod"
TESTMOD_SETTINGS_PAGE_LABEL="MCMBuilder Test Mod"
TESTMOD_SETTINGS_GROUP_1_LABEL="First settings group"
TESTMOD_SETTINGS_GROUP_2_LABEL="Second setting group"
```

There are two special config properties:

`"SaveConfigManager": "MCMBuilderClientTestModConfigManager"`

This references the config manager you want to use to get default config properties and thats responsible to save your properties to the user config directory.

`"EnableResetButton": "true"`

this just tells the builder if you want to display a reset button on your page.

### Elements
```
"HUNGRY": { "Type": "Checkbox" }, \\
"HUNGER_SCALE_NERD": { "Type": "Slider", "SliderMin": "0.0", "SliderMax": "1.0", "SliderStep":"0.1" }, \\
"HUNGER_SCALE": { "Type": "Spinner", "Options": "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" }, \\
"FOOD": { "Type": "Dropdown", "Options": "Apple, Chocolate, Burger" }, \\
```

These are the actual mcm elements that are displayed.
The identifiers (like "HUNGRY") are actual referencing config properties from the JsonConfigManager System.

localization works the same as with pages and groups:
Just define localization properties following the convention:
```
<PROPERTY_NAME>_LABEL
<PROPERTY_NAME>_TOOLTIP
```
e.g.
```
HUNGRY_LABEL="Hungry?"
HUNGRY_TOOLTIP="Are you hungry?"
```

## Client Side Integration
You will need to add the package
**JsonConfigManagerInterfaces** from **Musashis Mod Toolbox** to your mod project

![Screenshot](https://github.com/Musashi1584/MCMBuilderClientTestMod/blob/master/InterfacesPackage.jpg)

![Screenshot](https://github.com/Musashi1584/MCMBuilderClientTestMod/blob/master/InterfacesPackageEngineIni.jpg)

Then you need two config files and a localization file:
(this example assumes you mod safe name is **YourMod**

- `Config/XComJsonConfigManager.ini`
- `Config/XComMCMBuilder.ini`
- `Localization/MusashisModToolbox.int`

Lets see there contents in detail:

##### `XComMCMBuilder.ini`
```
[MusashisModToolbox.MCM_Builder_Screen]
+MCMBuilder="YourModMCMPage1" ;can be any name but have to match below and in the localization file
[YourModMCMPage1 JsonConfig_MCM_Builder]
+MCMPages = { \\
"TESTMOD_SETTINGS_PAGE":{ \\
"SaveConfigManager": "YourModConfigManager",\\
"EnableResetButton": "true", \\
"TESTMOD_SETTINGS_GROUP_1":{ \\
"HUNGRY": { "Type": "Checkbox" }, \\
"HUNGER_SCALE_NERD": { "Type": "Slider", "SliderMin": "0.0", "SliderMax": "1.0", "SliderStep":"0.1" }, \\
"HUNGER_SCALE": { "Type": "Spinner", "Options": "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" }, \\
"FOOD": { "Type": "Dropdown", "Options": "Apple, Chocolate, Burger" }, \\
}, \\
"TESTMOD_SETTINGS_GROUP_2":{ \\
"A_LABEL": { "Type": "Label" }, \\
}, \\
}, \\
}
```
##### `XComJsonConfigManager.ini`
```
[YourModConfigManager JsonConfig_ManagerDefault]
+ConfigProperties = {"HUNGRY":{"Value":"true"}}
+ConfigProperties = {"HUNGER_SCALE":{"Value":"1"}}
+ConfigProperties = {"HUNGER_SCALE_NERD":{"Value":"0.1"}}
+ConfigProperties = {"FOOD":{"Value":"Apple"}}
```
##### `MusashisModToolbox.int`
```
[YourModMCMPage1 JsonConfig_MCM_Builder]
TESTMOD_SETTINGS_PAGE_TITLE="Test Mod"
TESTMOD_SETTINGS_PAGE_LABEL="MCMBuilder Test Mod"
TESTMOD_SETTINGS_GROUP_1_LABEL="First settings group"
TESTMOD_SETTINGS_GROUP_2_LABEL="Second setting group"
HUNGRY_LABEL="Hungry?"
HUNGRY_TOOLTIP="Are you hungry?"
HUNGER_SCALE_LABEL="Hunger Scale"
HUNGER_SCALE_TOOLTIP="On a scale from 1 to 10"
HUNGER_SCALE_NERD_LABEL="Hunger Scale (Nerd)"
HUNGER_SCALE_NERD_TOOLTIP="On a scale from 0.0 to 1.0"
FOOD_LABEL="Food"
FOOD_TOOLTIP="Which food you want to eat?"
A_LABEL_LABEL="Label"
A_LABEL_TOOLTIP="Just a tooltip"
```

You can access the values in your mod like

```unrealscript
local JsonConfig_ManagerInterface ConfigManager;

ConfigManager = class'ConfigFactory'.static.GetConfigManager("MCMBuilderClientTestModConfigManager");
ConfigManager.GetConfigBoolValue("HUNGRY")
```

It will return the values that the user made in the mcm page or the default settings in case the user hasnt made any changes yet.
For more information about the config system see the [JsonConfigManager Documentation](https://github.com/Musashi1584/MusashisModToolbox/blob/master/JSONCONFIGMANAGER.md)

### Thats all!
The MCMBuilder in Musashis Mod Toolbox will take care of the rest.

![Screenshot](https://github.com/Musashi1584/MCMBuilderClientTestMod/blob/master/Screenshot.jpg)
Binary file added MCMBuilderClientTestMod/Screenshot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.