ID:273985
 
Heyo
I've just taken a quick look at a couple of macro libraries and thought I had the solution to my wanting to add macros @ runtime, problem.

However.
Whilst both Kakashi24142's and SqueakyReaper's libs "WORKED", they didn't actually add to the user's macro list (accessed by pressing F1)

Is there a way to add to said list? Or is that something that's protected so that developers don't screw with players' verbs?

And lastly, I'm guessing if I can't edit the list directly like that, the solution is to save what button does what as a list; then load the list of hotkeys @ login?

(for reference Im trying to create a simple way to set hotkeys from the game's menu - and will be doing so by displaying a picture of a keyboard and players will click the key they want to use, and select if they want repeat, on release, shift, alt, etc. But if anyone knows of a way to recreate the built in macro setting system (difference being I provide the command string & the player only chooses the key), I'd like to know. :)
You can't.
Look up the Skin Reference. There's an example on how to edit a macro at runtime. Also, look up the control_freak var.
A simple alternative is to restrict what keys a player can use, define all the macros yourself in the .dmf file, and just have the player change vars that determine what happens when a key is pressed. That way you're not changing macros at runtime.

If you do want to create new macros, here's how the Sidescroller library creates macros:

client
proc
set_macros()
// var/windows = winget(src, null, "windows")
// var/macros = params2list(winget(src, windows, "macro"))

// This should get us the list of all macro sets that
// are used by all windows in the interface. We can cut
// out the first winget call to get the set of windows.
var/macros = params2list(winget(src, null, "macro"))

var/list/keys = list("0","1","2","3","4","5","6","7","8","9","q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m","west","east","north","south","northeast","northwest","southeast","southwest","space","shift","ctrl","alt","escape","return","center","tab","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12",)
for(var/m in macros)
for(var/k in keys)
winset(src, "[m][k]Down", "parent=[m];name=[k];command=KeyDown+\"[k]\"")
winset(src, "[m][k]Up", "parent=[m];name=[k]+UP;command=KeyUp+\"[k]\"")


It creates key press and release macros for lots of keys, but you can use the same idea to create different events for a single key at a time.

This is how the Handy Stuff library does it:

<small>
//   Macros are created like this:
//
// client.add_macro("a", CTRL + ALT)
//
// When the player presses ctrl + alt + A their mob's macro()
// proc will be called. You can override this proc to implement
// the behavior to handle this event.

var
const
ALT = 1
CTRL = 2
SHIFT = 4
RELEASE = 8
REPEAT = 16

mob
proc
// The first argument is the key that was pressed. It's called
// "k" so it doesn't get confused with the mob's built-in key var.
// The second argument is a number that specifies the modifiers.
// See demo\demo.dm for an example of how to use macros.
macro(k, modifiers)

client
var
windows
list/macros
__macros_initialized = 0

New()
. = ..()
__init_macro_stuff()

verb
Macro(k as text, modifiers as num)
set hidden = 1
mob.macro(k, modifiers)

proc
add_macro(k, modifiers = 0)

if(!__macros_initialized)
__init_macro_stuff()

var/macro = __macro_name(k, modifiers)

for(var/m in macros)
winset(src, "[macro]", "parent=[m];name=[macro];command=Macro+\"[k]\"+[modifiers]")

remove_macro(k, modifiers = 0)
var/macro = __macro_name(k, modifiers)

for(var/m in macros)
winset(src, "[macro]", "parent=")

__macro_name(k, modifiers)
. = "[k]"
if(modifiers & ALT)
. = "Alt+[.]"
if(modifiers & CTRL)
. = "Ctrl+[.]"
if(modifiers & SHIFT)
. = "Shift+[.]"
if(modifiers & RELEASE)
. = "[.]+UP"
if(modifiers & REPEAT)
. = "[.]+REP"

__init_macro_stuff()
windows = winget(src, null, "windows")
macros = params2list(winget(src, windows, "macro"))
__macros_initialized = 1

</small>
In response to Forum_account
Just to be certain, your 2 snippets are both different methods of adding macros at runtime. ie. if I add them in the .dmf and use control freak, the snippets won't be needed?
In response to Saucepan Man
Saucepan Man wrote:
Just to be certain, your 2 snippets are both different methods of adding macros at runtime. ie. if I add them in the .dmf and use control freak, the snippets won't be needed?

Yes, that sounds right. You can either use code to add macros at runtime or you can create all macros you'll ever need in the .dmf file and have code that changes what each macro does. For example:

mob
var
attack_key = "x"
verb
KeyPress(k as text)
if(k == attack_key)
attack()


If every macro calls the KeyPress verb and passes it the name of the key that was pressed, you don't need to create new macros at runtime you'd just have to change the value of the attack_key var to change the key binding.
In response to Forum_account
Its 3am here and I'm very tired so thinking outside the box isnt my strong suit atm.
I can't see past a verb that will open a text input, and if you enter the letter "x" it will call attack()???
In response to Saucepan Man
Saucepan Man wrote:
Its 3am here and I'm very tired so thinking outside the box isnt my strong suit atm.
I can't see past a verb that will open a text input, and if you enter the letter "x" it will call attack()???

No, you'd define a macro in the .dmf file that calls the KeyPress verb and passes it "x". You can add "set hidden = 1" or whatever you need so the verb isn't accessible other than through the macros you define.
In response to Forum_account
Had a sleep now, and I understand; I think :)
In response to Saucepan Man
OK I've definitely got the gist of it; however I'm a little unsure on implimentation...
EDIT
mob
var
commandkey[]
verb
k(k as text)
usr.commandkey[k]
proc
AssignCommand()
src.commandkey["X"]=THIS()
proc
THIS()
world<<"Does every single verb ever now have to be a global proc?"


This works, but I don't like it.
In response to Saucepan Man
Here's a different approach.

snippet for setting macros at runtime
client
verb
setmacro(macro as text, command as command_text)
set name = ".setmacro"
macro=uppertext(macro)
// if( !(macro in changeable_macros) )
// src << "error message"
// return
var/macroset = "macro"
src << "\color(123,123,123).setmacro \"[macro]\" [command]"
winset(src,macro,"parent=[macroset];name=[macro];command=\"[escape_text(command)]\"")
proc
escape_text(txt)
txt = replacetext(txt,"\\","\\\\")
txt = replacetext(txt,"\"","\\\"")
return txt

replacetext(txt,txt1,txt2)
var/found = findtext(txt,txt1)
if(found)
return copytext(txt,1,found)+txt2+replacetext(copytext(txt,found+lentext(txt1)),txt1,txt2)
return txt

Modifications are necessary if you are not using the default skin.


You could simply enter something like this in your input control

.setmacro "A+SHIFT" say "hello world"


Other implementations
client
verb

Set_Macro()
var/macro = input(mob,"Input Macro") as text|null
if(isnull(macro))
return
var/command = input(mob,"Input Command") as text|null
if(isnull(command))
return
setmacro(macro,command)

Set_Macro_Select()
var/changeable_macros = list("A","B")
var/macro = input(mob,"Select Macro") as null|anything in changeable_macros
if(isnull(macro))
return
switch(alert(mob,"[macro]+SHIFT?","+SHIFT Modifier","Yes","No"))
if("Yes")
macro+="+SHIFT"
var/commands = list("attack","say \"hello world\"",".quit")
var/command = input(mob,"Select Command") as null|anything in commands
if(isnull(command))
return
setmacro(macro,command)

You may also use winget to get the macro / modifiers / desired command, if you like.