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

New Command: 2002 - Trigger Event At [x,y] #3183

Merged
merged 2 commits into from
Apr 10, 2024

Conversation

jetrotal
Copy link
Contributor

@jetrotal jetrotal commented Jan 7, 2024

@raw 2002, "", UseVarX,x UseVarY,y

Activate an event in map remotely, based on its x and y coordinates.
image

image

Command made by @MackValentine,
I only refactored it to fit the player specifications.

`@raw 2002, "", UseVarX,x UseVarY,y`

Activate an event in map remotely, based on its x and y coordinates.

Command made by @MackValentine,
I only refactored it to fit the player specifications.
@jetrotal jetrotal force-pushed the Mack-ActivateEventAt branch from ef8060f to 006066c Compare January 7, 2024 20:32
@Ghabry
Copy link
Member

Ghabry commented Feb 27, 2024

The latest Maniac stuff is finally becoming useful for us: This is all kinda undocumented but after 2 hours I got this beautiful Ui working.

grafik

@jetrotal
Copy link
Contributor Author

How hard is to start fiddling with those custom menus, do they only work on the japanese Maniacs?

Here some quick mockup I put together for this command.
image

@Ghabry
Copy link
Member

Ghabry commented Feb 29, 2024

I tried two days ago to patch the editor back to English. I didn't post about it so you can guess the result... The editor itself was mostly English again but the entire command window stayed Japanese. :/ So this relies on BingShan again to make it usable. Closed Source... Yeah......

The Ui is just Windows Forms. So they are just drag-and-dropped using the Visual Studio Designer. So they can contain anything you want.


Though the question is if that is worth the time. Another solution which would be also useful for our editor later is our own open source TPC replacement.

(2 commands of 154 done xD)

Teaser

grafik

@jetrotal
Copy link
Contributor Author

Oh! that's cool!
Do those scripting commands require a lot of handmade code?


Another question:
Will it be interpreted as string by the player or will it be compiled as commands, as tpc currently do?
Being read by the player would make it sucha powerful scripting language...

@Ghabry
Copy link
Member

Ghabry commented Feb 29, 2024

In needed two days to become used to the API and write wrappers so creating the binding code is not too ugly.

The script language is chaiscript (https://chaiscript.com/) with some custom modifications by me to make the syntax nicer. The syntax is JavaScript like. But simpler.

I will share some code when I found more event commands. Still figuring out some edge cases.

Currently this will be a code generator like TPC but I don't see a reason why this shouldn't be executable by the player directly later.

Code gen first is good for finding bugs :)

@Ghabry
Copy link
Member

Ghabry commented Mar 1, 2024

Example how to add a command (Not too useful yet as code is not released but just so you can see it).

Header:

#pragma once

#include "event_command.h"
#include "types.h"

namespace EasyScript {

class TriggerEventAt {
public:
	TriggerEventAt();
	TriggerEventAt X(const chaiscript::Boxed_Value& value);
	TriggerEventAt Y(const chaiscript::Boxed_Value& value);

	static void Register(chaiscript::ChaiScript& chai, EventCommand::List& commands);

	std::shared_ptr<EventCommand> cmd = std::make_shared<EventCommand>();
};

}

Source:

#include "trigger_event_at.h"
#include "chaiscript/chaiscript.hpp"
#include "dynamic_object.h"
#include "easyscript/event_command.h"
#include <lcf/rpg/eventcommand.h>

EasyScript::TriggerEventAt::TriggerEventAt() {
	cmd->SetDefaults(static_cast<EventCommand::Code>(2002), "", { 0, 0, 0, 0 });
}

EasyScript::TriggerEventAt EasyScript::TriggerEventAt::X(const chaiscript::Boxed_Value& value) {
	cmd->SetValueAndMode(0, 1, value);
	return *this;
}

EasyScript::TriggerEventAt EasyScript::TriggerEventAt::Y(const chaiscript::Boxed_Value& value) {
	cmd->SetValueAndMode(2, 3, value);
	return *this;
}

void EasyScript::TriggerEventAt::Register(chaiscript::ChaiScript& chai, EventCommand::List& commands) {
	chaiscript::ModulePtr m = std::make_shared<chaiscript::Module>();
	chaiscript::utility::add_class<TriggerEventAt>(*m, "__cls_TriggerEventAt",
	{
		chaiscript::constructor<TriggerEventAt()>()
	},
	{
		{chaiscript::fun(&TriggerEventAt::X), "x"},
		{chaiscript::fun(&TriggerEventAt::Y), "y"}
	}
	);
	chai.add(m);

	chaiscript::dispatch::Dynamic_Object o;
	o["trigger"] = chaiscript::var(chaiscript::fun([&](){
		auto evt = TriggerEventAt();
		commands.push_back(evt.cmd);
		return evt;
	}));
	chai.set_global(chaiscript::var(o), "@map");
}

Invocation in the script:

@map.trigger.x(123).y(345)
@map.trigger.y($v(222)).x($vv(333))

@jetrotal
Copy link
Contributor Author

jetrotal commented Mar 1, 2024

Looks interesting.
I supose those commands could be generated from a .csv file.
Just combining what is "type" or "...IsVar" with the respective values parameters

@Ghabry
Copy link
Member

Ghabry commented Mar 1, 2024

yeah currently I'm not auto-generating it. But will do it for most of the simple commands. Still streamlining the syntax a bit.

Before:

void EasyScript::TriggerEventAt::Register(chaiscript::ChaiScript& chai, EventCommand::List& commands) {
	chaiscript::ModulePtr m = std::make_shared<chaiscript::Module>();
	chaiscript::utility::add_class<TriggerEventAt>(*m, "__cls_TriggerEventAt",
	{
		chaiscript::constructor<TriggerEventAt()>()
	},
	{
		{chaiscript::fun(&TriggerEventAt::X), "x"},
		{chaiscript::fun(&TriggerEventAt::Y), "y"}
	}
	);
	chai.add(m);

	chaiscript::dispatch::Dynamic_Object o;
	o["trigger"] = chaiscript::var(chaiscript::fun([&](){
		auto evt = TriggerEventAt();
		commands.push_back(evt.cmd);
		return evt;
	}));
	chai.set_global(chaiscript::var(o), "@map");
}

After:

void EasyScript::TriggerEventAt::Register(chaiscript::ChaiScript& chai, EventCommand::List& commands) {
	Bind<TriggerEventAt, void, TriggerEventAt()>(
		chai, commands,
		"TriggerEventAt", "map", "trigger",
		&TriggerEventAt::X, "x",
		&TriggerEventAt::Y, "y"
	);
}

@jetrotal
Copy link
Contributor Author

jetrotal commented Mar 1, 2024

looking better!
Dummy question, what is value at cmd->SetValueAndMode(0, 1, value);

Probably the value from user input? 🤔

@Ghabry
Copy link
Member

Ghabry commented Mar 1, 2024

That's the argument passed in to the function

@map.trigger.x(1).y($v(2))

Function X receives number 1 and Function Y receives Variable(2).

SetValueAndMode checks the type of the passed in value and sets the correct mode automatically based on it.

@Ghabry
Copy link
Member

Ghabry commented Mar 2, 2024

Okay and I enhanced the API again: The binding API "introspects" now the class to bind everything. Of course will only work for events that are quite "simple" but this is the case for 90% of them ;).

Example for TriggerEventAt:

Header:

#pragma once

#include "easyscript/forward.h"
#include "easyscript/parameter.h"

namespace EasyScript {

class TriggerEventAt {
public:
	TriggerEventAt();

	std::shared_ptr<EventCommand> cmd = std::make_shared<EventCommand>();

	static void Register(chaiscript::ChaiScript& chai, EventCommandList& commands);

	static constexpr std::array name = { "TriggerEventAt", "map", "trigger" };

	static constexpr const std::array param = std::to_array<Parameter>({
		{ "x", 0, 1, 0 },
		{ "y", 0, 3, 2 },
	});
};

}

Source:

#include "trigger_event_at.h"
#include "chaiscript/chaiscript.hpp"
#include "easyscript/binding.h"
#include "easyscript/event_command.h"

EasyScript::TriggerEventAt::TriggerEventAt() {
	cmd->SetDefaults(static_cast<Code>(2002), "", { 0, 0, 0, 0 });
}

void EasyScript::TriggerEventAt::Register(chaiscript::ChaiScript& chai, EventCommandList& commands) {
	BindAuto<TriggerEventAt, void, TriggerEventAt()>(chai, commands);
}

More complex example: PlayBgm

Header:

#pragma once

#include "easyscript/forward.h"
#include "easyscript/parameter.h"

namespace EasyScript {

class PlayBgm {
public:
	PlayBgm(StringArg value);

	std::shared_ptr<EventCommand> cmd = std::make_shared<EventCommand>();

	static void Register(chaiscript::ChaiScript& chai, EventCommandList& commands);

	static constexpr std::array name = { "PlayBgm", "music", "play" };

	static constexpr const std::array param = std::to_array<Parameter>({
		{ "fadein", 0, 0, 4, 1 },
		{ "volume", 100, 1, 4, 2 },
		{ "tempo", 100, 2, 4, 3 },
		{ "balance", 50, 3, 4, 4 }
	});
	static constexpr const StringParameter string_param = {nullptr, 5, 4, 0};

	static std::string FromCommand(const EventCommand& command);
};

}

Source:

#include "play_bgm.h"
#include "chaiscript/chaiscript.hpp"
#include "easyscript/binding.h"
#include "easyscript/event_command.h"
#include "easyscript/forward.h"

EasyScript::PlayBgm::PlayBgm(StringArg value) {
	cmd->SetDefaults(Code::PlayBGM, "", { 0, 100, 100, 50 });
	string_param.Set(*cmd, value);
}

void EasyScript::PlayBgm::Register(chaiscript::ChaiScript& chai, EventCommandList& commands) {
	BindAuto<PlayBgm, StringArg, PlayBgm(StringArg)>(chai, commands);

	BindNamespaceFunctions(
		chai, "music",
		[&](){
			auto evt = PlayBgm(chaiscript::Boxed_Value(std::make_shared<const std::string>("(OFF)")));
			commands.push_back(evt.cmd);
			return evt;
		}, "stop"
	);
}

std::string EasyScript::PlayBgm::FromCommand(const EventCommand& command) {
	if (string_param.GetMode(command) == 0 && command.string == "(OFF)") {
		return "@music.stop";
	}

	return {};
}

Copy link
Member

@Ghabry Ghabry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is like call event but it checks if the event passes the start conditions and which page should be run so is imo a useful addition.

Not sure if it should be ActivateEventAt or TriggerEventAt but that is only nitpicking.

Btw I would prefer to keep this 2002 cast for now and then do the enum stuff in liblcf in one batch.

(My script stuff will take at least 2-3 months to be usable so is not worth to wait for it 🙈)

@jetrotal
Copy link
Contributor Author

jetrotal commented Mar 7, 2024

@Ghabry We have ways of activating commands right now, So it's no problem aproving those new commands. TriggerEventAt is also a good name!

@Ghabry Ghabry force-pushed the Mack-ActivateEventAt branch from efe97d7 to 20b4215 Compare April 8, 2024 21:29
@Ghabry
Copy link
Member

Ghabry commented Apr 8, 2024

Going to merge this when the build passed.

Note that starting from Player 0.8.1 this command will be subject to "opt-in" for EasyRPG extensions.

Please add this to your INI file to be future proof:

[Patch]
EasyRPG=1

@Ghabry Ghabry changed the title New Command: 2002 - Activate Event At [x,y] New Command: 2002 - Trigger Event At [x,y] Apr 10, 2024
@Ghabry Ghabry merged commit 62e28c5 into EasyRPG:master Apr 10, 2024
12 checks passed
@florianessl
Copy link
Member

I'm prototyping some enhanced debugging flags and stumbled upon this.
One thing that sticks out, that this behaves exactly, as if it was the player was in front of the event and pressed the decision key:

SaveEvent::triggered_by_decision_key is set to true, so any condition branch that checks for this, will evaluate to true. Is this wanted?
Game_Player::CheckEventTriggerThere always will set the event to automatically face the player, right before the commands are scheduled for the interpreter. Maybe also unwanted behavior?

And now for the reason why I looked at this command...:
It might be useful, if a new field was defined for "SaveEvent" which indicates that the event was triggered this way. Something like "easyrpg_triggered_indirectly" or maybe even a bitflag field which might hold several values. Otherwise there would be no indication in the callstack how this code was started.

@Ghabry
Copy link
Member

Ghabry commented Jun 5, 2024

You are right, both of these behaviours should not happen (or at least make it possible to configure them). This shows that even simple commands need lots of testing.

I'm currently creating a TestGame to test the new commands before doing further inclusion (next one is "Wait for Single Movement" so this doesn't happen again. Sorry :/

@jetrotal jetrotal deleted the Mack-ActivateEventAt branch November 28, 2024 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

5 participants