Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Jaber's Debug Console v1.02

Jaber's Debug Console

This is the greatest debug console you will ever see.
Why? Because I made it.

Features:
-Omnipresent
-Omnipotent
-Omniscient

No, seriously.
Omnipresent
It works anywhere at anytime from the title to battletests to your menus.

Omnipotent
It is an on-demand script call so it can do basically anything.

Omniscient
It has a special @routine variable. If it is nil, it does nothing. If it is not, it evals every frame.
Punch in:
@routine = '@textdata[0] = $game_variables[5].to_s; write("")'
OH SNAP CONSOLE NOW WORKS AS A DEBUG MONITOR
Use $console.writeline instead of p to non-intrusively debug your scripts, or use $console.log to log the debug lines to a file.
Set @debug_show to true and the console will stay visible when inactive so you can monitor any debug lines from your scripts as they happen.

HOLY SHIT JABER, SIGN ME UP
Setup is simple:
1. Delete the following from Scene_Map:
Code:
    # If debug mode is ON and F9 key was pressed

    if $DEBUG and Input.press?(Input::F9)

      # Set debug calling flag

      $game_temp.debug_calling = true

    end
2. Paste '$console = Window_Console.new' after the '$game_system = Game_System.new' line in Scene_Title's main and battle_test methods.
3. Paste this above main:
Code:
#Jaber's Debug Console v1.02

class Window_Console < Window_Base

  

  attr_accessor :debug_show, :memory, :routine, :is_active

  

  def initialize

    super(-16, -16, 672, 512)

    self.contents = Bitmap.new(width - 32, height - 32)

    self.contents.font.name = "Arial"

    self.contents.font.size = 18

    self.opacity = 0

    self.z = 99999

    @increment = 18

    @textdata = [""]

    @linemax = 20

    @memory = []

    @is_active = false

    @debug_show = false

    @key = Win32API.new("user32", "GetKeyState", "i", "i")

    @state = Win32API.new("user32", "GetKeyboardState", "p", "i")    

    @text = []

    @keys = []

    @insert_position = 0

    @memory_position = -1

    @routine = nil

    @clear_lines = false

  end

  

  def writeline(text = nil)

    @textdata[0] = text unless text == nil

    @textdata.unshift("")

    @textdata.pop if @textdata.length > @linemax

    self.contents.clear

    linecounter = 1

    for lines in @textdata

      linecounter += 1

      self.contents.draw_shadowed_text(4, 478 - (@increment * linecounter), 640, 32, lines)

    end

  end

  

  def write(endchar = "|")

    self.contents.fill_rect(0, 478 - (@increment + (@increment / 2)), 640, 32, Color.new(0,0,0,0))

    self.contents.draw_shadowed_text(4, 480 - (@increment * 2), 640, 32, @textdata[0].to_s.dup.insert(@insert_position, endchar))

  end

  

  def update

    if @routine != nil

      begin

        eval(@routine)

      rescue Exception

        @routine = "@is_active = true; writeline('--Exception raised on routine eval!'); @routine = nil"

      end

    end

    if Input.trigger?(Input::F9) or @is_active

      @is_active = true

      @textdata[0] = ""

      write

      @keys[120] = 2

    end

    self.visible = (@is_active or @debug_show)

    super if self.visible

    while @is_active

      Graphics.update

      console_update

      super

    end

  end

  

  def key_down(key)

    return @keys[key] > 0

  end

  

  def key_toggle(key)

    return @key.call(key) % 2 == 1

  end

  

  def key_trigger(key)

    return @keys[key] == 1

  end

  

  def key_release(key)

    return @keys[key] == -1

  end

  

  def key_hold(key, hold)

    return (@keys[key] >= hold)

  end

  

  def key_repeat(key, hold = 10, throttle = 3)

    return (key_trigger(key) or (key_hold(key, hold) and @keys[key] % throttle == 0))

  end # Input.repeat: hold=15 throttle=4

  

  def update_keyboard

    @keyboard = ' '*256

    @state.call(@keyboard)

    @keyboard = @keyboard.scan(/./)

  end

  

  def update_keys

    update_keyboard

    i = 0

    for key in @keyboard

      @keys[i] = 0 if @keys[i] == nil

      if (key.unpack("B*")[0].to_i > 1) or @keys[i] < 0

        @keys[i] += 1 if @keys[i] < 15000

      elsif @keys[i] > 0

        @keys[i] = -1

      end

      i += 1

    end

  end

  

  def update_text

    @text = []

    caps = key_toggle(20) != key_down(16)

    for i in 65..90

      @text.push(i + (caps ? 0 : 32)) if key_repeat(i)

    end

    caps = key_down(16)

    for i in 48..57

      @text.push(i) if key_repeat(i)

    end

    @text.push(caps ? 58 : 59) if key_repeat(186)

    @text.push(caps ? 43 : 61) if key_repeat(187)

    @text.push(caps ? 60 : 44) if key_repeat(188)

    @text.push(caps ? 95 : 45) if key_repeat(189)

    @text.push(caps ? 62 : 46) if key_repeat(190)

    @text.push(caps ? 63 : 47) if key_repeat(191)

    @text.push(caps ? 126 : 96) if key_repeat(192)

    @text.push(caps ? 123 : 91) if key_repeat(219)

    @text.push(caps ? 124 : 92) if key_repeat(220)

    @text.push(caps ? 125 : 93) if key_repeat(221)

    @text.push(caps ? 34 : 39) if key_repeat(222)

    @text.push(32) if key_repeat(32)

    @text = @text.pack("c*")

    if caps

      @text.gsub!(/1/) { "!" }

      @text.gsub!(/2/) { "@" }

      @text.gsub!(/3/) { "#" }

      @text.gsub!(/4/) { "$" }

      @text.gsub!(/5/) { "%" }

      @text.gsub!(/6/) { "^" }

      @text.gsub!(/7/) { "&" }

      @text.gsub!(/8/) { "*" }

      @text.gsub!(/9/) { "(" }

      @text.gsub!(/0/) { ")" }

    end

    return !@text.empty?

  end

    

  def console_update

    update_keys

    last_text = @textdata[0].dup

    last_insert = @insert_position

    if update_text

      if @insert_position == @textdata[0].length

        @textdata[0] += @text

      else

        @textdata[0].slice!(@insert_position, @text.length) if key_toggle(45)

        @textdata[0].insert(@insert_position, @text)

      end

      @insert_position += @text.length

    end

    if key_repeat(8) and @insert_position > 0

      @insert_position -= 1

      @textdata[0].slice!(@insert_position, 1)

    end

    @textdata[0].slice!(@insert_position, 1) if (key_repeat(46) and @insert_position < @textdata[0].length)

    if key_repeat(35)

      @insert_position = @textdata[0].length

    elsif key_repeat(36)

      @insert_position = 0

    elsif key_repeat(37)

      @insert_position = [@insert_position - 1, 0].max

    elsif key_repeat(39)

      @insert_position = [@insert_position + 1, @textdata[0].length].min

    elsif key_repeat(38)

      @memory_position = [@memory_position + 1, @memory.length - 1].min

      @textdata[0] = @memory[@memory_position].dup if @memory_position >= 0

      @insert_position = @textdata[0].length

    elsif key_repeat(40)

      @memory_position = [@memory_position - 1, -1].max

      @textdata[0] = @memory_position == -1 ? "" : @memory[@memory_position].dup

      @insert_position = @textdata[0].length

    end

    if key_repeat(13)

      unless @textdata[0] == ""

        @memory.unshift(@textdata[0])

        @memory.pop if @memory.length > @linemax

      end

      writeline

      begin

        text_eval = @textdata[1].dup

        text_eval.slice!(0,5) if @textdata[1].index(/[Ss][Hh][Oo][Ww] /) == 0

        val = eval(text_eval)

        writeline("=>" + val.inspect.to_s) if @textdata[1].index(/[Ss][Hh][Oo][Ww] /) == 0

      rescue NameError

        writeline("--NameError: No such method or variable")

      rescue SyntaxError

        writeline("--SyntaxError: Cannot eval this line")

      rescue Exception

        writeline("--Cannot eval this line")

      end

      @insert_position = 0

      @memory_position = -1

      write

    elsif last_text != @textdata[0] or last_insert != @insert_position

      write

    end

    if @clear_lines

      @clear_lines = false

      self.contents.clear

      @textdata = [""]

      write

    end

    if key_trigger(120)

      @is_active = false

      @keys = []

      @textdata[0] = ""

      @insert_position = 0

      @memory_position = -1

      write("")

      Input.update

    end

  end

  

  def log(filename, data, mode = 'a')

    file = File.open(filename, mode)

    file.puts(data)

    file.close

  end

  

  def clear_lines

    @clear_lines = true

  end

  

end

 

class Bitmap

  

  def draw_shadowed_text(x, y, wid, hei, text, align = 0, thickness = 1, direction = [2,3,6], olcolor = Color.new(0,0,0), incolor = Color.new(255,255,255))

    color = self.font.color.dup

    self.font.color = olcolor

    draw_text(x - thickness,y + thickness,wid,hei,text, align) if direction.include?(1)

    draw_text(x,y + thickness,wid,hei,text, align) if direction.include?(2)

    draw_text(x + thickness,y + thickness,wid,hei,text, align) if direction.include?(3)

    draw_text(x - thickness,y,wid,hei,text, align) if direction.include?(4)

    draw_text(x + thickness,y,wid,hei,text, align) if direction.include?(6)

    draw_text(x - thickness,y - thickness,wid,hei,text, align) if direction.include?(7)

    draw_text(x,y - thickness,wid,hei,text, align) if direction.include?(8)

    draw_text(x + thickness,y - thickness,wid,hei,text, align) if direction.include?(9)

    self.font.color = color

    self.font.color = incolor if incolor.is_a?(Color)

    draw_text(x,y,wid,hei,text, align)

    self.font.color = color

  end

  

end

 

module Input

  class << self

    

    alias console_update update unless method_defined?(:console_update)

    def update

      console_update

      $console.update

    end

    

  end  

end
4. You're done!

Using the console
Press F9 to open/close the console. Gameplay will be paused while it is opened.
Type 'show' at the start of the line to display the output from that line, e.g. 'show $game_actors[1].hp'
Left/Right arrows move the cursor left/right. Up/Down arrows access previously entered commands.
You can clear the console window by typing in 'clear_lines'
The console has support for all my keys except the numberpad. Insert, delete, home, end, etc. all work.

Screenshots
A.K.A. PICTURES OF TEXT ON TOP OF PICTURES (Click to enlarge)



Changelog
1.02 - No longer requires replacing Input.update calls, thanks to Glitchfinder
1.01 - Replaced heavy outlines with shadowed text for better readability and less lag
 

regi

Sponsor

Hey, that's a pretty neat concept! I'm sure processing actual RGSS commands while running the game will make bugtesting much nicer. My only suggestion would be to add a small semi-transparent window behind the text; it doesn't seem to blend well with certain backgrounds/tilesets.
 
I will see what I can do about the transparent window; most likely it will end up being a fullscreen black image with a variable to control the opacity of it.
 
While I can appreciate the uses this script offers to advanced game designers, I can't help but disagree with a large portion of the original post.

The omnipresent bit is true, assuming you follow the instructions to the letter when you install it. However, what the instructions fail to mention is that as soon as someone adds in a custom scene (assuming they had already installed this script and believed they were done), the omnipresent bit will come crashing down into falsehood, because this script will no longer function in every scene. That's because what the instructions don't mention is that you are actually required to add a bit of code to every scene in the game, including custom ones you add down the line.

The omnipotent bit is an outright fabrication. If you need an example of something it can't do, check out my script console, which has a pair of commands that allow you to operate from within any portion of the game code, rather than simply operating as a separate class. That kind of ability would allow someone to do something like modify class level variables on the fly, which this script cannot do unless the variable has an attr_accessor or a method that allows it to be set.

The omniscient bit is also a stretch. Instead of making it omniscient, you're giving it OCD by making it do the same thing over and over again. A useful ability in programming, but still a far cry from omniscience.

Then, I have to disagree with your statement that setup is simple. For most users, the only thing that qualifies as simple setup would be a plug 'n play script, where you just drop it in and it works. This script not only requires you to drop in the code itself, it requires the user to make an edit to every script within a specific category, along with any custom ones added later. (Which is not mentioned)

Finally, I would like to bring to your attention the fact that this console does not in fact support all keys except the ones you listed. Instead, it supports all keys on a USA 101 format keyboard. I specifically make certain to test anything I make that uses keyboard input on a variety of foreign keyboard formats by changing the settings in Windows, and I often offer my scripts for testing to BlueScope, who lives in Germany and does in fact use a German keyboard. He's the one who first brought to my attention certain problems like the fact that foreign keyboards use different layouts and that things like dead keys do not work as intended. In fact, I have only seen one input script for RM that handled dead keys correctly, and I will freely admit I did not make it.

Anyway, I'm sorry for the heavy criticisms. I was certainly impressed with the extremely low line count, especially when compared to my own console, which puts a much heavier emphasis on functionality than it puts on line count. (And yet, it still runs just fine) I'm also pleasantly surprised to find that you managed to counter at least some of the crashes caused by incorrect code, although I have yet to do more thorough testing on that matter. (Though, to be fair, it would be nice if you had included a verbose error option as well)
 
It should be able to read all keystrokes no matter what your layout is... in theory. Whether they do what you're expecting is another story, but I don't have access to different keyboard layouts to test anything. I wouldn't even know where to begin on that... It should also catch pretty much any error you throw at it other than an infinite loop, which it might catch anyways. There's not a whole lot more I can do for that since it already has a 'rescue Exception' branch on it.

Anyways, the omni- thing is called showmanship. Need to pay less attention to the titles and more to the subtext for them, e.g. 'It is an on-demand script call so it can do basically anything.' If you're going to nitpick you might as well complain that 'because I made it' does not make it the best. It does everything it says it can. Being that it is a script call, I am expecting the people who use it to have some idea of what they are doing. If the installation and use are too complicated for someone, they are probably not going to get much use out of it. It has more functionality than most people will have use for, if you're clever with it. For example, you could add an 'ensure' bit to RMXP's main script that logs the console to a file when it crashes so you can read all the debug output from your scripts. Much less annoying than going through a bunch of print windows that you have to pay attention to because you're waiting for it to crash.

Honestly I just made it for myself and was like "oh hey this doesn't actually depend on anything in my game, guess I should release it" and so here it is. o_o/
 
I probably should have mentioned that my critique on the omnipresent, omnipotent, and omniscient portions are because I feel there is a difference between showmanship and ridiculousness. You're not a snake oil salesman, so I would assume you don't have to resort to the same kinds of tactics.

That said, it's pretty easy to test other keyboards if you're on windows. Just go to the language settings in your control panel, and you can change the keyboard to one that you don't have plugged in. Then, just go into the ease of access section of accessories, and open up the virtual keyboard. Make certain that you change the keyboard format before you open up RMXP, because it sticks with the one that was set when you opened it, and you will have to close it to try again. I recommend trying the German and UK keyboard layouts, because both of them have potential issues.

Anyway, I still do think this is a pretty good script. My objections were more with the way it was presented than the script itself, as was evidenced with the massive post above. To be fair, I'm not that good at offering criticisms on the code itself in most cases, so if you would want that kind of thing, just ask BlueScope about it.
 
I can have ridiculous showmanship!
For that is one of my sovereign rights as emissary of the moon-people D:<
Also it was meant to sound kinda ridiculous!

Edit - From looking at your input module, you managed to do what I was trying to do in the first place, which was to embed the console update into Input.update. So, now you don't have to replace Input.update with $console.update anymore! :O
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top