This is a FAQ page about creating plug-ins using C#.
The content of this FAQ is based on the inquiries we have received from our users and the responses we have received from our development team.
Please refer to the How to Use Plug-ins section.
You can use a different editor by specifying an association for the sln file other than Visual Studio.
"Conversation" can be achieved with the following method. The position of the window (top, middle, bottom) changes when the 0's are set to 1, 2, 3, etc.
mapScene.ShowDialogue(msg, 0, MenuControllerBase.WindowTypes.DIALOGUE, new AbstractRenderObject.GameContent.DialogueCharacterProperty() new AbstractRenderObject.GameContent.DialogueCharacterProperty(), mapChr.guId);
The DialogueCharacterProperty() parameter can be used to assign a graphic of the speaking cast, but the parameters are generally linked to what can be specified in the panel.
"Message" is the following method. As with conversation, the second parameter, 0, specifies the window position.
mapScene.ShowMessage(stringAttr.value, 0, MenuControllerBase.WindowTypes.MESSAGE, mapChr.guId);
In addition, the "ticker" will be the following method.
mapScene.ShowTelop(msg, 0, MenuControllerBase.WindowTypes.TELOP, new AbstractRenderObject.GameContent.TelopProperty(), mapChr.guId);
ShowDialogue and ShowMessage return an ad hoc id in the return value, so you can use this to execute
mapScene.IsQueuedDialogue(id)
and
mapScene.IsQueuedMessage(id)
to see if the displayed conversation or message is still being displayed (not closed).
This is not possible at the Ver. 1.9 stage.
You can use
Graphics.DrawString (with borders)
or
Graphics.DrawStringSoloColor (without borders).
At this time (ver. 1.9), we do not release methods for text decoration by formatting.
(If you need formatted text decoration, please wait for the release of SpriteManager.)
Please use the following:
var render = catalog.getItemFromName<Yukar.Common.Rom.RenderSettings>("Rendering Setting Name"); mapScene.mapDrawer.setRenderSettings(render);
Please use one of the following two options.
mapChr.playMotion("MotionName") mapScene.hero.playMotion(("MotionName", 0.2f, true, lockMotion))
lockMotion = Whether to prevent disarming by walking, etc. / locked by true, false for no locking.
To assign a key and apply it, please use the following.
mapScene.hero.getModelInstance().setMorphBlend("KeyName", 0.5f);
To assign a clip and apply it, please use the following.
The parameters are the target, the clip name, the interpolation time, and whether to reset other blendshapes.
ScriptRunner.AddBlendShapeTask(mapScene.hero, "ClipName", 4, false);
Please use the sample code by binding it to an event.
MotionUtil.cs
GetMotionLoopCount to obtain the number of motion loops.
In the case of a one-shot, the motion can be considered complete if it is 1.
PlayMotionImmediately starts motion playback without blending.
Use this when the normal motion playback panel is too much of a distraction from the blending.
Please use the following.
mapChr.SetSubGraphicOpacity
Subgraphic positions are interpreted in mapChr.DisplayObjects as a matrix get function for updating the coordinates of each display object, so they cannot be changed dynamically.
It can be obtained using the following.
((Yukar.Common.Resource.GfxResourceBase)mapChr.getGraphic()).SubGraphics[SubgraphicNumber].useTranslateOnly
Changes are not recommended, as they will directly rewrite the resource.
The following pos, rot, and scale manage these information.
((Yukar.Common.Resource.GfxResourceBase)mapChr.getGraphic()).SubGraphics[SubgraphicNumber]
Changes are not recommended, as they will directly rewrite the resource.
The following useLight manages the on/off information.
((Yukar.Common.Resource.GfxResourceBase)mapChr.getGraphic()).SubGraphics[SubgraphicNumber]
Changes are not recommended, as they will directly rewrite the resource.
This can be done by the following way.
var spIndex = 10; // Sprite Number var guid = new Guid("c15f94b6-d4a0-46c4-bc43-2db2f64471ac"); // GUID for slice animation var zoom = 100; var x = 640; var y = 360; mapScene.spManager.ShowPicture(spIndex, guid, zoom, zoom, Microsoft.Xna.Framework.Color.White, new Yukar.Common.Rom.Script.StringAttr("walk"), x, y);
The GUID of the slice animation can be obtained by right-clicking on the Resources screen > Copy GUID.
Unlike Graphics.drawImage, this can be executed only once and will continue to be displayed thereafter.
It can be moved with "mapScene.spManager.Move" and removed with "mapScene.spManager.Hide".
This is the class used in the "Display Image" event panel.
This feature is not available in the engine because it uses an editor-specific rendering path (pickup buffer).
Please use the following.
mapScene.map.mapCameraRange
if(mapScene.map.mapCameraRange == Rectangle.Empty) is true, it is unset (unrestricted).
Set the model GUID in
mapScene.mapDrawer.renderSettings.skyModel
and then after setting the environment map texture GUID in
mapScene.mapDrawer.renderSettings.reflection
execute
mapScene.mapDrawer.setRenderSettings(mapScene.mapDrawer.renderSettings);
The following code can be used to obtain the cast type of mapChr.
var type = mapChr.rom?.CastType ?? Yukar.Common.Rom.CastType.ALLY;
Refer Collision Detection between Cast Members and Events to make a determination.
It is possible with the following code.
var viewer = mapScene.fieldBattleViewer; viewer.zoomedScale = 2.0f; // Scale at just the right time to come out viewer.damageScale = 1.0f; // Eventual scale viewer.zoomTime = 0.1f; // Time to reach eventual scale viewer.drawTime = 1.0f; // Time until damage disappears viewer.damageColor = Microsoft.Xna.Framework.Color.White; // Damage color viewer.criticalDamageColor = Microsoft.Xna.Framework.Color.Red; // Critical damage color viewer.criticalDamageColor = Microsoft.Xna.Framework.Color.Red; // Critical damage color
You can also create your own damage rendering process using the attached script.
ChangeFieldBattleViewer.cs
Since the judgment is made randomly based on the critical rate, it cannot simply be determined to occur.
It would be a good idea to use state changes to temporarily increase or decrease the critical rate.
You can still obtain the information by using the following.
mapChr.rom.InvincibleTime
However, the change is not recommended because it would directly modify the Database.
You can switch using the following.
GameMain.instance.data.start.controlMode = Yukar.Common.Rom.GameSettings.ControlMode.NORMAL; GameMain.instance.data.start.controlMode = Yukar.Common.Rom.GameSettings.ControlMode.RESIDENT_EVIL_LIKE;
The scale itself can be obtained with GameMain.instance.catalog.getGameSettings().TalkColliderScale.
However, changes are not recommended since they directly edit the value of the Game Definition.
It is not recommended to change the values for GameSettings in general, as any changes will be retained even after the test play is completed.
It can be obtained and changed using the following.
mapScene.mapFixCamera
In addition, individual enable/disable for each camera operation can be obtained and changed by using the following.
owner.data.start.camLockX owner.data.start.camLockY owner.data.start.camLockZoom owner.data.start.camLockReset
You can use the following to obtain the states of each.
catalog.getGameSettings().CameraAvoidObjects catalog.getGameSettings().CameraAvoidTerrain
However, changes are not recommended since they directly edit the value of the Game Definition.
It is possible to obtain information using the following.
GameMain.instance.catalog.getGameSettings().isTpsMode
However, changes are not recommended since they directly edit the value of the Game Definition.
Not possible. Please use the Game Definition settings as they are.
It is possible to obtain the speed using the following.
GameMain.instance.catalog.getGameSettings().DefaultRotateSpeed
However, changes are not recommended since they directly edit the value of the Game Definition.
Cannot be done.
Instead, create your own process to rotate the camera according to the value of Input.GetAxis() and change the rotation speed accordingly.
It is possible to obtain information using the following.
catalog.getGameSettings().meta.buildVer
The information can then be displayed on the layout by storing it in a variable as shown below.
GameMain.instance.data.system.SetVariable("VariableName", catalog.getGameSettings().meta.buildVer.ToString(), Guid.Empty, false);
However, running C# on the default title screen is not possible as of Ver 1.9.
Therefore, the title screen should be skipped in the Game Definition and used with the in-game title screen in the event.
A sample code is attached.
ConfigGetter.cs
It can be obtained by executing the C# method "GetAutoDash" after assigning it.
Please use the following.
MapCharacter.getRigidBody().setFriction(FrictionValue)
However, since MapCharacter controls friction accordingly, it may become invalid at a moment's notice.
The following method can be used.
mapChr.setOpacityMultiplier(0.5f); // Fully transparent at Transparency 0f
Regarding setOpacityMultiplier, the player is constantly monitoring and applying transparency changes due to state changes, so any changes will be overwritten.
Try setting every frame with AfterDraw().
Please use the code below.
// To open var lyt = catalog.getLayoutProperties().AllLayoutNodes.FirstOrDefault(x => x.Name == "ItemSelect"); if (lyt != null) { GameMain.instance.ShowFreeLayout(lyt.Guid); } // To close var lyt = GameMain.instance.freeLayouts.FirstOrDefault(x => x.LayoutNode.Name == "ItemSelect"); if (lyt != null) { GameMain.instance.HideFreeLayout(lyt.LayoutGuid); }
You can do so as shown below, but since the assignment to positionAnchorTag directly rewrites the Layout Tool's settings (which are then kept as rewritten) and does not take effect until the layout is redisplayed, it is better to use the Position operation.
var lyt = GameMain.instance.freeLayouts.FirstOrDefault(x => x.LayoutNode.Name == "ItemSelect"); if (lyt != null) { var node = lyt.LayoutDrawer.ParseNodes().FirstOrDefault(x => x.MenuItem.name == "Item Details"); node.Position = new Microsoft.Xna.Framework.Vector2(256, 256); // To move directly node.MenuItem.positionAnchorTag = "Rewrite Special Coordinate Tags"; }
You can access the currently displayed layout by the following ways.
[BakinFunction(Description = "Obtain the container management number of the selected subcontainer\nSet the action of the subcontainer to "Call Common Event" and call the following method in that event")] public int GetIndex() { // First, obtain the main menu manager var lc = mapScene.menuWindow as LayoutMenuController; var lm = lc.mainMenu; // Track and obtain open layouts derived from the main menu // (If it's being called from item select, it should end up in item select.) while (lm.NextlayoutManager != null) lm = lm.NextlayoutManager; // Obtain the currently selected index from the Drawer var drawer = lm.LayoutDrawer; GameMain.PushLog(DebugDialog.LogEntry.LogType.EVENT, "drawer", drawer.GetSelectIndex().ToString()); return drawer.GetSelectIndex(); }
Furthermore, if you wish to force the main menu to close after indexing, you can use the following method.
[BakinFunction] public void CloseMainMenu() { // First, obtain the main menu manager var lc = mapScene.menuWindow as LayoutMenuController; var lm = lc.mainMenu; lm.HideAll(); }
Incidentally, to obtain the currently selected index from a free layout, the following is used.
[BakinFunction] public int GetIndexForFreeLayout() { // First, obtain a free layout manager var lm = GameMain.instance.freeLayouts.FirstOrDefault(x => x.LayoutNode.Name == "Name of the Free Layout you want to obtain"); if (lm == null) { GameMain.PushLog(DebugDialog.LogEntry.LogType.EVENT, "drawer", "error"); return -1; } // Obtain the currently selected index from the Drawer var drawer = lm.LayoutDrawer; GameMain.PushLog(DebugDialog.LogEntry.LogType.EVENT, "drawer", drawer.GetSelectIndex().ToString()); return drawer.GetSelectIndex(); }
This is the case for the field named tags.
You can access it by the following.
catalog.getItemFromName<Yukar.Common.Rom.Cast>("Hero").tags
The skills that a cast member learns are in Cast.availableSkills. They can be obtained as follows.
var learnSkills = catalog.getItemFromName<Yukar.Common.Rom.Cast>("Ken").availableSkills;
Skills are stored in the form of GUIDs, so Achievement Level and the skills themselves can be obtained by further querying the Catalog as shown below.
if (learnSkills.Count > 0) { var skill = catalog.getItemFromGuid(learnSkills[0].skill) as Yukar.Common.Rom.NSkill; GameMain.PushLog(DebugDialog.LogEntry.LogType.EVENT, "C#", "Achievement Levels : " + learnSkills[0].level + " / Skill Name : " + skill.name); }
A complete list of all skills can be obtained by the following way.
var allSkills = catalog.getFilteredItemList<Yukar.Common.Rom.NSkill>().Cast<Yukar.Common.Rom.NSkill>();
You can obtain the EXP of the first monster by the following way.
BattleSequenceManagerBase.Get().EnemyViewDataList[0].monster.exp
You can also obtain the first drop item of the first monster by the following way.
var guid = BattleSequenceManagerBase.Get().EnemyViewDataList[0].monster.dropItems[0].item; var name = catalog.getItemFromGuid(guid)?.name;
When actually using the EnemyViewDataList or dropItems, check the upper limit by looking at the Count.
BGM as a system has only one playback line, so it cannot be done with Audio.PlayBGM.
First, use the following method to start playback with zero volume as a sound effect (the loop setting is still reflected in this case).
var id = Audio.LoadSound(guid); Audio.PlaySound(id, 0, 0, 1);
Then use the following to manipulate the volume every frame to bring the volume of the new BGM closer to 1 while bringing the previous BGM closer to 0.
Audio.SetSEVolume(id, volume);
You can change it with the following code.
GameMain.instance.data.system.currentBattleCameraSet = "CameraSetName";
The camera will be reflected at the time of camera switching, but if you want it to be reflected immediately, you can use the following code.
mapScene.applyCameraToBattle();
Please note, however, that this is only effective from the C# assigned to the battle event.
First load the texture using the following code in Start(), for example.
tex = catalog.getItemFromName<Yukar.Common.Resource.Texture>(""TextureResourceName""); Graphics.LoadImage(tex);
Then, use the following at the scene you wish to render (Update, AfterDraw, etc.).
Graphics.DrawImage(tex, x, y);
After use, release the image used when the event is discarded as shown below.
public override void Destroy() { Graphics.UnloadImage(tex); }
It is available for use.
It can be used like mapChr.CreateEvent(guid, dir);.
mapChr.getDirectionRad() should be passed for dir if you want it to face the same direction as the source.
guid is the GUID of the cast.
It would be better to use catalog.getItemFromName("name of the cast you want to obtain").guId to find it and pass it.
Once a GUID is created for a cast, it does not change. Therefore, it is faster to write down the GUID by outputting a log, etc. and pass it without looking for it in the catalog by new Guid("GUID string").
getRelativeParam60FPS is the elapsed time from the previous frame converted to 60FPS.
In other words, this method returns the time taken for one frame (0.016666666 for 60FPS) multiplied by 60.
Within the C# script Update(), frameCount++; will add 1 to frameCount, and assuming no processing slowdown, it will be called 60 times per second, increasing by 60 per second.
However, this method will result in the addition of non-"60" values such as "30" or "120" per second when processing is slow or when the refresh rate is higher than 60.
Therefore, instead of frameCount++, frameCount += getRelativeParam60FPS() can be used to ensure that 60 is added per second even if the time taken per update is not 1/60 second.