Read-only archive of the Armageddon Games Forums Archive (2000–2026) · About this archive

ZShell: A Live debugging and cheat shell for ZC for Necromancer (2.556)+

2 replies · 39,733 views · Started 02 November 2018

[FONT=Georgia]Some of you are probably aware, that since I've added the ability to type into ZScript, I've made a few text parsing modules.

Recently, I've given some thought to making a more portable version of my complex debug shell.

The one that I use is rather tightly woven into other things, and it isn't very easy for most people todeduce how it works. Therefore, I started again clean, writing something designed to run a single instruction in a shell, rather than a macro script.

This is now, tested and working. Press F7 to open the debug shell. [/font]

[FONT=Georgia]Demo Quest | Docs[/font]

[FONT=Georgia]FFC and Additional Visual and Audio Commands[/font]
[video=youtube_share;gIHVy6StR7w]https://youtu.be/gIHVy6StR7w[/video]


[FONT=Georgia]In version 1.11, I added the ability to enqueue a series of commands at one time.[/font]
[FONT=Georgia]Press the down arrow key to enqueue an instruction![/font]

[FONT=Georgia]Enqueued Commands[/font]
[video=youtube_share;T6UecY8XE9c]https://youtu.be/T6UecY8XE9c[/video]

[FONT=Georgia]Basic Commands[/font]
[video=youtube_share;-0iSkymXrmk]https://youtu.be/-0iSkymXrmk[/video]


[FONT=Georgia]Visual Commands[/font]
[video=youtube_share;1G_hE1-W5Rc]https://youtu.be/1G_hE1-W5Rc[/video]

[FONT=Georgia]Sequence Scripts[/font]
[FONT=Georgia]You can store a sequernce of instructions as a sequence script, that you can run at any time by invoking[/font] [FONT=Courier New]RunSequence,id[/font][FONT=Georgia].
[/font]
[video=youtube;H-rLwwPq1oE]https://www.youtube.com/watch?v=H-rLwwPq1oE[/video]

[FONT=Georgia]You may screate up to ten sequence scripts, that function like macros, and you can save them whenj you save the quest, so that you can easily design test macros (like shell/batch scripts).[/font]

[FONT=Georgia]If anyone is curious on how to do new, insane things, have a look at this beastie! 😉[/font]

[FONT=Georgia]I designed the code in the link (above) so that when you press the F7 key (you can change this in the CFG settings), ZC opens a shell window. From there, you may type in any of the following commands:

Supported Instructions[/font]

[FONT=Georgia][FONT=Georgia][b][u][/font]Syntax and Usage[/u][/font][/b]

[FONT=Georgia]Tokens terminate by either a comma, or reaching the end of the string (NULL character).

If anyone is interested in using this, or if any of you are curious and have questions, please let me know. I may expand it to support multiple instructions and macros in the future, if there is any demand for that.[/font]

[FONT=Georgia]It also does not yet pause the action in ZC. I plan to add some ZScript functions to suspend all of ZC, except for scripts, just as the combo types Freeze All, and Freeze All (except FFCs), as Game->Freeze(int type, bool state), at some future point, so that scripts needn't use messy combo modification (or steal FFCs), in order to pause the game action.[/font]

[FONT=Georgia]The code for this should be both highly legible, and easy to modify. Adding instructions is relatively simple and straightforward, although you do need to add them in a few places--similar to adding ZASM and ZScript in the ZC Source files pertaining to ZScript (ffscript):[/font]

[FONT=Georgia]To Expand the Instructions[/font]
1. [FONT=Courier New]Define [/font]a [FONT=Courier New]case [/font][FONT=Georgia]value after:[/font]
[FONT=Courier New]define LINKITEM = 12; //item, (BOOL), on / off[/font]

[FONT=Georgia]2. In the switch statement inside of int[/font] [FONT=Courier New]num_instruction_params(int instr)[/font],
[FONT=Georgia]add a[/font] [FONT=Courier New]case [/font]value for the new instruction, [FONT=Georgia]and return the number of params that it uses.

3. Determine the actual in-use instruction identifier, and create a [FONT=Courier New]case[FONT=Georgia] [/font][/font]set flow/entry for its characters, in [FONT=Courier New]int match_instruction(int token)[/font], [FONT=Georgia]returning the new[/font] [FONT=Courier New]case [/font][FONT=Georgia]value on a match.[/font]

3. Determine the actual in-use instruction identifier, and create a [FONT=Courier New]case[FONT=Georgia] [/font][/font]set flow/entry for its characters, in [/font][FONT=Courier New]int match_instruction(int token)[/font], [FONT=Georgia]returning the new[/font] [FONT=Courier New]case [/font][FONT=Georgia]value on a match.[/font]

[FONT=Georgia]4. In the [/font][FONT=Courier New]switch [/font]statement inside of v[FONT=Courier New]oid execute()[/font][FONT=Georgia], add the new [/font][FONT=Courier New]case label[/font], [FONT=Georgia]and give it instructions to call a normal ZScript function.[/font]

[FONT=Georgia]5. Args and Stack Size
The present stack supports up to two args, so, if you need more, you will need to modify the value of [/font][FONT=Courier New]MAX_ARGS[/font]. [FONT=Georgia]The size of the stack is automatically determined and generated on compilation.[/font]


[FONT=Georgia]Script Syntax and Structure[/font]
[FONT=Georgia]This script makes use of a few new syntax features in ZScript, that require the latest Necromancer alpha builds. These include both typedef instructions, and script-local variables, arrays, and constants.[/font]

[FONT=Georgia]The details on these new mechanical ZScript components are a bit lengthy, so, for the sake of those shy of [/font][FONT=Courier New]<tl;dr>[/font],[FONT=Georgia] I'll put it all in a spoiler tag...[/font]

[spoiler]
[FONT=Georgia]These are not the same as 'local variables' and ;local arrays', that you would list inside the [/font][FONT=Courier New]run() [/font][FONT=Georgia]function. They are still global, but their identifiers are within the local namespace of the script.[/font]

[FONT=Georgia]Essentially, this script turns an ffc script into a pseudo-namespace in syntax. (It's primarily for the sake of organising identifiers, and preventing global namespace pollution, although it isn't technically a real [/font][FONT=Courier New]namespace[/font].)

[FONT=Georgia]1. You may access to the[/font] [FONT=Courier New]stack[][/font] [FONT=Georgia]globally as [/font][FONT=Courier New]debugshell.stack[].[/font]

[FONT=Georgia]2. Likewise, all of the local defines, and SP are available as [/font][FONT=Courier New]debugshell.DEFINITION[/font] [FONT=Georgia]and[/font] [FONT=Courier New]debugshell.SP,[/font][FONT=Georgia] respectively.[/font]

[FONT=Georgia]3. Essentially, jyou access the script-local functions, variables, and arrays in precisely the same manner as the exant script-local functions. Keep in mind that when you do this, that variables and arrays become part of the global memory map, so you should limit this to things that have a purpose at a global scope.[/font]

[FONT=Georgia]Constants (defines) still use no stack space, and they are resolved during compilation.[/font]

[FONT=Georgia]4. From within the script that contains the local declarations, you call all of these by their local identifier, so script name resolution is not required. Thus, the[/font] [FONT=Courier New]namespace script debugshell [/font][FONT=Georgia]calls [/font][FONT=Courier New]stack[][/font], [FONT=Courier New]WINDOW_F_KEY[/font], [FONT=Georgia]and [/font][FONT=Courier New]execute()[/font]; [FONT=Georgia]but from another script (or from the[/font] [FONT=Courier New]global namespace[/font],[FONT=Georgia] you would use [/font][FONT=Courier New]debugshell.stack[][FONT=Georgia],[/font][/font] [FONT=Georgia]debugshell[/font].[FONT=Courier New]WINDOW_F_KEY,[/font] [FONT=Georgia]and [/font][FONT=Courier New]debugshell.execute()[FONT=Georgia],[/font][/font][FONT=Georgia] respectively.[/font]

[FONT=Georgia]The [/font][FONT=Courier New]global script test[/font][FONT=Georgia], does precisely this, when it calls[/font] [FONT=Courier New]debugshell.process()[/font].

[FONT=Georgia]5. Note that none of this namespace handling is mandatory to make this script feasible. It simply makes it cleaner (IMO), and ensures that the global namespace of the user isn't polluted by conflicting identifiers. I've started doing this for all of my script sets--putting functions and large arrays inside of class scripts, namespace scripts, and struct scripts.[/font]

[FONT=Georgia]All of these are still[/font] [FONT=Courier New]ffc [/font][FONT=Georgia]scripts--they can be any type, but I arbitrarily use [/font][FONT=Courier New]ffc[/font][FONT=Georgia]--but they don't often have anything inside the body of[/font] [FONT=Courier New]run()[/font], [FONT=Georgia]living their lives as fancy containers.[/font]

[FONT=Georgia]Those of you [/font][FONT=Georgia]worried abour running out of [/font][FONT=Courier New]ffc [/font][FONT=Georgia]script slots[/font][FONT=Georgia], should also note, that you do not need to assign a script to a slot to use its namespace. You need only assign scripts to slots if you need to execute the[/font] [FONT=Courier New]run()[/font] [FONT=Georgia]function of that script.[/font]

[FONT=Georgia]I think that covers the syntax in use here, and I hope that all--or at least some--of you, find it interesting and useful.[/font] [/spoiler]

[FONT=Georgia]Using[/font]

[FONT=Courier New]Press F7 to open the shell.
Type an instruction.
Press the DOWN ARROW KEy to ENQUEUE the instruction, or;
Press ENTER to execute the instruction.
If you have enqueued instructions, press ENTER either at the
end of an instruction, or on an empty line to EXECUTE
all of your enqueued instructions.[/font]

[FONT=Courier New][FONT=Georgia]Traces[/font]

You may print a line of text, or a literal value directly
to allegro.log using the following syntax:

%s
This traces all characters that follow it, to
allegro.log as a string.
It eats all leading spaces and characters,
so all of these are identical:
%sPRINTME
%s PRINTME
%s: PRINTME

The output of this will be:
LOG: PRINTME

%d
This traces a literal value (integer or float)
to allegro.log.
It eats all leading spaces and characters,
so all of these are identical:
%100.500
%s:100.500
%s 100.500
%s: 100.500

The output of this will be:
LOG: 100.500

Traces cannot be enqueued at this time.

[FONT=Georgia]Sequence Scripts[/font]

You may save a sequence of instructions that you have entered
into the shell into one of TEN SEQUENCE SLOTS.

To do this, after entering a series of commands, with a BLANK LINE,
press the CONTROL KEY and one of the main number keys (above the home row,
not on the number pad), 1 through 0.

The number key that you press corresponds to the SEQUENCE slot that your
sequence script is using.

If you save the quest, your sequence script will save with it.

To run a sequence script, open the shell, and do not add any INSTRUCTIONS ON THE STACK:
Type the command: RunSequence,id

[example--

RunSequence,1

--end example]

This will immediate;y run Sequence Script 1.
[/font]


[FONT=Georgia]Supported Instructions[/font]

[FONT=Courier New]Link's Position
w : Warp Link to a specific dmap and screen
po : Reposition Link on the screen.
mx : Move link by +/-n pixels on the X axis.
my : Move link by +/-n pixels on the Y axis.

Counters
h : Set Link's HP to a specific value.
m : Set Link's Magic to a specific value.
a : Set the current number of Arrows
b : Set the current number of Bombs
r : Set the current number of Rupees
mb : Set the current number of Max Bombs
ma : Set the current number of Max Arrows
mr : Set the current number of Max Rupees
k : Set the current number of Keys
co : Set an arbitrary counter to a specific value.
rh : Refill Link's HP to Max.
rm : Refill Link's HP to Max.
rc : Refill a specific counter to Max.
mh : Set Link's Max HP.
mm : Set Link's Max MP
mc : Set the maximum value of a specific counter.

Items
lk : Set the current number of Level Keys for a specific level ID.
lm : Set if the MAP item for a specific Level is in inventory.
lc : Set if the COMPASS item for a specific Level is in inventory.
lt : Set if the TRIFORCE item for a specific Level is in inventory.
lb : Set if the BOSS KEy item for a specific Level is in inventory.
itm : Set the state of a specific item in Link's inventory.

Link's States
hb : Set if Link uses a full tile hitbox. (hitbox)
d : Set if Link can move diagonally.
inv : Set Link's Invisible state.

Create Objects
crn : Create an npc.
cri : Create an item.

FFC Values
fd : Set the Data value of one ffc.
fs : Set the Script value of one ffc.
fc : Set the CSet of an ffc.
fx : Set the X component of an ffc.
fy : Set the Y component of an ffc.
fvx : Set the X Velocity Component of an ffc.
fvy : Set the Y Velocity Component of an ffc.
fax : Set the X Accel. Component of an ffc.
fay : Set the XYAccel. Component of an ffc.
ffl : Set an ffc flag state true or false.
fth : Set the TileHeight of an ffc.
ftw : Set the TileWidth of an ffc.
feh : Set the EffectHeight of an ffc.
few : Set the EffectWidth of an ffc.
fl : Link an ffc to another, or clear a link.
fm : Write to the Misc[] values of an ffc.

Audio
pls : Play a sound effect.
plm : Play a MIDI.
dmm : Set the MIDI for a specific DMap to a desired ID.

Visual
pal : Change a DMap palette.
mon : Set a monochrome graphical state.
hu : Set a specific hue effect.
t : Set a specific tint effect.
cl : Clear hue/tint.

Misc.
run : Attempt to run an ffc script.
save : Save the game.[/font]

[FONT=Courier New]LITERAL VALUES
Use these as srg values, if desired, instead of literals:
lx : The current X-coordinate for Link.
ly : The current Y-coordinate for Link.[/font]

[FONT=Georgia]Syntax and Usage[/font]

[FONT=Courier New]Link's Position
w,1,2 : w,0,32 : Warps Link to DMap 0, screen 32.
po,1,2 : po,60,12 : Set Link's X to 60, and his Y to 12
mx,1 : mx,12 : moves Link +12 pixels on his X coordinate
mx,-1 : mx,-12 : moves Link -12 pixels on his X coordinate
my,1 : my,12 : moves Link +12 pixels on his Y coordinate
my,-1 : my,-12 : moves Link -12 pixels on his Y coordinate

Counters
h : h,amount
h : h,amount
m : m,amount
a : a,amount
b : b,amount
r : r,amount
mb : mb,amount
ma : mb,amount
mr : mr,amount
k : k,amount
co : co,counter_id,amount
rh : Refill Link's HP to full.
rm : Refill Link's Magic to full.
rc,1 : rc,2 : Refill Counter CR_BOMBS to full.
mh,1 : mh,64 : Set Link's HP to 64
mm,1 : mm,256 : Set Link's Max Magic to 256
mc,1,2 : mc,2,16 : Set the Bombs counter max to 16.

Items
lk : lk,level_id,,amount
lm : lm,level_id,true|false
lc : lc,level_id,true|false
lt : lt,level_id,true|false
lb : lb,level_id,tru|false
itm,1,true : itm,6,true : Set the white sword true in inventory.
itm,1,false : itm,6,true : Set the white sword falsein inventory.

Link's States
hb : hb,true|false
d : d,true|false
inv,true : Set Link's Invincibility state true.
inv,false : Set Link's Invincibility state false.

Create Objects
cri,1,2,3 : cri,id,x,y : cri,id,lx,ly : Create an item (id) at x,y
crn,1,2,3 : cri,id,x,y : Create n npc (id) at x,y

FFC Values
fc : fc,ffc_id,cset
fx : fx,ffc_id,x
fy : fy,ffc_id,y
fvx : fvx,ffc_id,vx
fvy : fvy,ffc_id,vy
fax : fax,ffc_id,ax
fay : fay,ffc_id,ay
ffl : fl,ffc_id,flag_index,(true|false)
fth : fth,ffc_id,tileheight
ftw : ftw,ffc_id,tilewidth
feh : feh,ffc_id,effectheight
few : few,ffc_id,effecteidth
fl : fl,ffc_id,link_id
fm : fm,ffc_id,misc_index,value
fd : fd,ffc_id,combo_id
fs : fsscript_id

Audio
pls : pls,sound_id
plm : plm,midi_id
dmm : dmm,dmap_id,midi_id

Visual
hu : hu,red,green,blue
t : t,red,green,blue [,distributed (true|false)]
pal : pal,-1,6 : Sets the current DMap palette to 6.
: pal,3,6 : Sets the palette for DMap ID 3 to Palette ID 6.
mon : mon,5 : Sets monochrome (violet hue) mode on. (0 to disable).
cl : cl

Misc.
run : run,script_id
save : Save the game.[/font]