Jump to content

kal

Member
  • Content Count

    123
  • Joined

  • Last visited

  • Days Won

    1

kal last won the day on February 5 2012

kal had the most liked content!

About kal

  • Rank
    Advanced Member

Profile Information

  • Gender
    Male

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. kal

    Random Ruby and RGSS3 Tutorials

    Tutorial 2 - Fun with ObjectSpace Today we're gonna look at a cool module called ObjectSpace that was added to Ruby 1.9. ObjectSpace allows you to see how many objects have been created by the Ruby interpreter and what their types are. With ObjectSpace you can also iterate through every object known to the Ruby interpreter like if they were in one big array which can sometimes be handy as we'll see later on in this tutorial. First here here's how to see how many objects have been created in total: ObjectSpace.count_objects => {:TOTAL=>49106, :FREE=>6590, :T_OBJECT=>5543, :T_CLASS=>807, :T_MODULE=>38, : T_FLOAT=>744, :T_STRING=>13174, :T_REGEXP=>27, :T_ARRAY=>13512, :T_HASH=>71, :T_ STRUCT=>24, :T_BIGNUM=>1, :T_FILE=>4, :T_DATA=>5889, :T_MATCH=>12, :T_COMPLEX=>1, :T_NODE=>2650, :T_ICLASS=>19} TOTAL is how many object have been created during the entire run-time of the program and FREE is how many have been garbage collected. Then there is the ObjectSpace#each_object method which can be used to iterate over every alive object in the program. For example: ObjectSpace.each_object do |object| puts object end You can pass a class name to each_object to only iterate over objects that inherit from that class. For example to iterate over all the strings in the program do this: ObjectSpace.each_object(String) do |string| puts string end Now let's say we want to store a reference to each string in the program in an array for future use. One way to do it is this: strings = [] ObjectSpace.each_object(String) do |object| strings << string end But there's an easier way to do this because calling each_object without a block it returns an enumerator. I won't go in to exactly what enumerators are and how to use them in this tutorial, but in short they are external iterators. If you have programmed in Java they are similar to Java's Iterator class. Anyway, an enumerator can be directly converted to an array with to_a, so we can simplify the above code like this: strings = ObjectSpace.each_object(String).to_a Now that you know the basics of ObjectSpace let's build something that we can use in Ace with it! What I had in mind was a class that can get Window instances given a coordinate (an X and Y value). So the class should work like this: window_info = WindowInfo.new some_window = window_info.window_at(100, 100) # the values here are the X and Y coordinate, respectively, of a point that is within the bounds of the window When you start to write a class it's sometimes helpful to use "scripting by assumption" which means that you start at some level of abstraction and write code as if you had already implemented all the lower level methods. Using this approach, we can write the window_at method like this: class WindowInfo def window_at(x, y) windows = get_windows_at_position(x, y) sorted = windows.sort_by { |window| window.z } sorted.first end end The window_at method first gets an array of all window objects at the given coordinate using the helper method get_windows_at_position. After that is done we need to return one window only (because there could be more than one window at a given screen coordinate) so we want to return the window that has the highest Z coordinate i.e. the one that is "on top". We can do this by sorting the windows using their Z value and then simply returning the first element of the sorted array. So next let's implement get_windows_at_position (again employing scripting by assumption): class WindowInfo def window_at(x, y) windows = get_windows_at_position(x, y) sorted = windows.sort_by { |window| window.z } sorted.first end private def get_windows_at_position(x, y) windows = get_all_windows windows.find_all do |window| !window.disposed? && within_bounds?(window, x, y) end end end Here we delegate the job of getting all the window objects to another method, get_all_windows. Next once we have all the windows we want to filter out the ones that are disposed and the ones that are not within the bounds of the coordinate. We can do that by calling the find_all method on the window array. find_all will return an array containing only the elements for which the block returned true. In the block we return true only if the window is not disposed, and if the window is within the bounds of the coordinate. Now let's code our two remaining helpers, get_all_windows and within_bounds? class WindowInfo def window_at(x, y) windows = get_windows_at_position(x, y) sorted = windows.sort_by { |window| window.z } sorted.first end private def get_windows_at_position(x, y) windows = get_all_windows windows.find_all do |window| !window.disposed? && within_bounds?(window, x, y) end end def get_all_windows ObjectSpace.each_object(Window).to_a end def within_bounds?(window, x, y) x_range = (window.x)..(window.x + window.width) y_range = (window.y)..(window.y + window.height) x_range.include?(x) && y_range.include?(y) end end As you can see, get_all_windows simply uses the ObjectSpace module's each_object method to return an array of all Window objects (that is, all Window objects and all objects which inherits from Window). within_bounds? is a little more involved. It starts by creating two range (http://ruby-doc.org/core-1.9.3/Range.html) objects using the .. syntax. These ranges make up the rectangle of the window as it is drawn on the screen. The window.x and window.y values is where the upper left corner of the window is placed on the screen, so if we start at that point and add the width and height we get the window's rectangle. Then it's simply a matter of checking if the x and y coordinates are withing the x and y ranges. Now our WindowInfo class is complete! However, it's kind of hard to try it out right now. Sure, we can use it inside a script in the editor but it's not that useful. To make it more interesting we'll use Alex, an interactive console I wrote some time ago. You can get Alex here: https://gist.github.com/kl/2044957 Alex works like a poor man's version of the awesome Pry library: http://pryrepl.org/ It has a few features you can read about in the script comments but it basically allows you to pause script execution at any time and type in code that will be execute in the current context. Once you have Alex installed, you can start an Alex session by either calling binding.alex from anywhere inside any script, or by pressing the D key on the keyboard when the game is running. We'll use the latter approach in this tutorial. So with Alex and our newly created WindowInfo class ready to go, start the game and go into the menu screen (and make sure that you enabled the RGSS console window). Now if you press D you should see that the game freezes and the console window should print out alex: Now you can start typing in some code and it will evaluate and print out the return value. For example: alex: 2 + 2 => 4 so let's use Alex to get a reference to the Window_MenuCommand window (the window in the upper left corner). alex: $wmc = WindowInfo.new.window_at(100, 100) => #<Window_MenuCommand:0x83c5e1c> So we give the coordinate (100,100) and our WindowInfo class gives back the Window_MenuCommand object that occupies that coordinate! Yay! Note that I am using a global variable here, something which is not normally advisable, and that's i because it's an easy way to make sure we can get to the object later on. I wouldn't do this in production code, but for simple testing like this it's fine. Now that we have a reference to the window object, we can play around with it a bit. For example we can make it invisible: alex: $wmc.visible = false => false Now when you type this in you won't see a change in the game window because the game is currently in a frozen state. To switch back to the game exit out of Alex by typing exit: alex: exit Now the window should disappear. Or what about changing it's position (to get back to Alex just press D again): alex: $wmc.visible = true => true alex: $wmc.y = 150 => 150 That concludes this tutorial on ObjectSpace and how it can be used to hold of object references! You could extend this example a lot further if you wanted, for exampling if you have a mouse script installed you could create a debugging system where you could click on the screen to select windows and even re-position windows with the mouse.
  2. Really cool idea! This could bring a whole new dimension of tactics to the battle system but there will be some challenges as this type of system will need some kind of enemy AI or else it would probably be easy to exploit. I've written a very basic WIP prototype that you can play around with: https://gist.github.com/kl/5815417 Right now the position system is implemented for enemies (not yet for actors) and it does not yet support negative values. The default behavior for enemies is simple: move forward until there is a skill that can reach an actor and then keep using that skill(s). Later on this behavior could be expanded on to make the enemies smarter. To use the script you have to create a skill that represents the moving forward action. You can set the skill id in the script configuration. Then for the skill in the database you should make it Skill Type: None, Scope: The User, and add some kind of dummy effect (like Recover HP 0 %) You can set the range for enemy skills by adding a tag like this: <bp_skill_range NUMBER>
  3. kal

    Show image in equip window!

    Hi, try this: https://gist.github.com/kl/5787548
  4. kal

    Instrument Script (patly completed)

    When it says expecting keyword_end it means you have missed an end somewhere. Every class, module, method, block etc should have an end to indicate where it ends. It looks like the class Scene_Instrument is missing it's end keyword.
  5. ^^^ I also felt compelled to do something so I wrote this : module CommonEventOnHitKal TAG_NAME = "ceoh" class IDExtractor def get_common_event_id_for(user) rpg_actor = $data_actors[user.id] extract_common_event_id(rpg_actor.note) end private def extract_common_event_id(note) id = note[/<#{TAG_NAME}\s+(\d+)\s*>/, 1] id.nil? ? nil : id.to_i end end class CommonEventRunner def run(common_event_id) page = make_page(common_event_id) $game_troop.interpreter.setup(page.list) end private def make_page(id) page = RPG::Troop::Page.new # 117 => common event code, 0 => indent, id => id of common event page.list.unshift(RPG::EventCommand.new(117, 0, [id])) page end end @id_extractor = IDExtractor.new @common_event_runner = CommonEventRunner.new def self.run_event_for(user) event_id = @id_extractor.get_common_event_id_for(user) @common_event_runner.run(event_id) if event_id end end class Game_Battler alias_method :item_apply_ceoh_kal, :item_apply def item_apply(user, item) item_apply_ceoh_kal(user, item) # item id 1 is Attack if user.is_a?(Game_Actor) && item.id == 1 && @result.hit? CommonEventOnHitKal.run_event_for(user) end end end Actually this is a variation of this request by Kcaz64 http://www.rpgmakervxace.net/topic/16007-common-event-when-state-ends-is-removed/ So maybe a more general script that can run common events in many different situations would be useful? To use this script create a tag in the actor's note box like this: <ceoh 4> "ceoh" is the name of the tag (you can change this in the script if you want) and the number is the id of the common event that should run when the actor hits with the Attack skill.
  6. kal

    Instrument Script (patly completed)

    I can see one problem right away and that is that you are using integer symbols as keys in the hash. A symbol cannot start with an integer and that's why you get the syntax error. Let me try and explain symbols real quick. A symbol is almost like a string, and a string is a sequence of characters. You write strings in double or single quotes in Ruby and symbols start with a colon. "string" :symbol for now just think of a symbol as a string but you are only interested in it's name. For example, when using the symbol :symbol we only ever care about the symbol's value (it's name), and we will never add characters to the symbol, change lowercase to uppercase, look at individual characters and so on, all which you can do with a string. So back to your code. You have this {:1 => :A} which is not legal because a symbol cannot start with a number, it must start with a letter. So to fix this you could do this: {:s1 => :A} or this {"1" => :A} in the former snippet I added a letter to the start of the symbol making it legal. In the latter I change the symbol to a string and strings are allowed to start with anything so "1" is a perfectly legal string. That should fix the first error but there has to be more to the script because what you posted will not do much of anything by itself.
  7. Here's give this a go: module StateRemoveCommonEventsKal TAG_NAME = "srce" class IDExtractor def get_common_event_id_for_state(state_id) rpg_state = get_rpg_state(state_id) extract_common_event_id(rpg_state.note) end private def get_rpg_state(state_id) $data_states.find do |rpg_state| rpg_state && rpg_state.id == state_id end end def extract_common_event_id(note) id = note[/<#{TAG_NAME}\s+(\d+)\s*>/, 1] id.nil? ? nil : id.to_i end end class CommonEventRunner def run(common_event_id) page = make_page(common_event_id) $game_troop.interpreter.setup(page.list) end private def make_page(id) page = RPG::Troop::Page.new # 117 => common event code, 0 => indent, id => id of common event page.list.unshift(RPG::EventCommand.new(117, 0, [id])) page end end @id_extractor = IDExtractor.new @common_event_runner = CommonEventRunner.new def self.run_event_from_state(state_id) event_id = @id_extractor.get_common_event_id_for_state(state_id) @common_event_runner.run(event_id) if event_id end end class Game_BattlerBase alias_method :erase_state_srce_kal, :erase_state def erase_state(state_id) StateRemoveCommonEventsKal.run_event_from_state(state_id) erase_state_srce_kal(state_id) end end make a state and put a tag in the state notebox like this: <srce [common event id]> For example: <srce 4> will run the common event with id 4 when the state is removed. If you want to use another tag name simply change the TAG variable in the script. Let me know if it works or not.
  8. Well there are a couple of things you need to consider. First how exactly did you change the variable from the event commands? Are you changing the move speed of the player or of some event (which both inherit from Game_CharacterBase)? So let's assume that you want to change the move speed of the player. You can get to the player object by using the global variable $game_player. Then to set the move speed do this: $game_player.move_speed = 4.3 Only this will fail until you add an attribute_accessor to the Game_CharacterBase class (or you could add it to Game_Player if you only wish to change the move speed of the player). So attribute_accessor basically means that you can access (that is get the value of or change the value of) an instance variable from outside the object, which is what we are doing when we write $game_player.move_speed = 4.3 So go to Game_CharacterBase and add the following line attr_accessor :move_speed Then you can set the move speed by typing $game_player.move_speed = 4.3 If you want to change it for events you first need to get a reference to the event object you want to change, and then do the same thing.
  9. Try this: def run_common_event(id) # 117 => common event code, 0 => indent, id => id of common event list = [RPG::EventCommand.new(117, 0, [id])] $game_troop.interpreter.setup(list) end Basically what you want to do is to make a call to $game_troop.interpreter.setup and pass along and array that consists of the RPG::EventCommand that will in turn call your common event. When you do this a new fiber is created in $game_troop's interpreter object and this fiber (a fiber is like a lightweight thread) executes the common event in parallel to the main thread that runs the battle. This is all you need to do execute a common event in battle. Take a look at RPG::Troop::Page and RPG::EventCommand for a better understanding of how it works. One thing though, if you want your common event to execute after the battle is supposed to end, say for example after your party killed all the enemies you need to take a few extra steps. The default implementation will just exit the battle even if a fiber (that could be executing for example a common event) is running. To change this first make the fiber instance variable readable in the Game_Interpreter class like this: class Game_Interpreter # Make fiber readable so we can check if an event is running # before finishing the battle. attr_reader :fiber end Then alias and change one method in BattleManager like this: alias_method :process_victory_cevent, :process_victory def process_victory return unless $game_troop.interpreter.fiber.nil? process_victory_cevent end Now the original process_victory is only run if there is no fiber running in $game_troop.interpreter.
  10. So what you are doing is that you are overriding the default implementation of the module method level in Vocab. You can call module methods in two ways: # first way Vocab.level # second way Vocab::level Your first line (puts Vocab::level(0)) will fail because the code will go in to the elsif statement and attempt to call the module method level again (in other words, it will try to call itself). However, this fails because there is no argument for that line, and it needs one argument because that's how you defined the level method. Your second line succeeds because it calls the module method level_a which does not require any argument (as defined in the editor). Personally, I prefer to use the dot syntax when calling a module method and not :: which is used for accessing constants in a module.
  11. Ok I think I know what you're missing. I don't think you are checking the enemy note tags correctly. Also, when is the play_snowbgm method called in your script? It looks like it's not called at all. So when you put text in an enemy's note box, that text is saved along with various other information about that enemy and put into a RPG::Enemy object at the start of the game. Notice that this RPG::Enemy object is not the same as the Game_Enemy object. You can think of the RPG::Enemy object as the data object that keeps track of all the data associated with a particular enemy (for example the note box text) and the Game_Enemy as the game object that has the state (variables), methods etc. So what you need to do is to check the note-box text for the enemies that you are about to fight, see if the text contains "snow_bgm" and call the play_snowbgm method if it does. Here's some code that does that: $game_troop.members.each do |game_enemy| id = game_enemy.enemy_id rpg_enemy = $data_enemies[id] note = rpg_enemy.note if note.include?("snow_bgm") play_snowbgm break end end So basically this code iterates over all Game_Enemy objects in $game_troop, gets their associated RPG::Enemy object (each Game_Enemy instance has one RPG::Enemy instance), gets the note text and finally checks if the text includes "snow_bgm" and calles play_snowbgm if it does. Put this code at the beginning of the pre_battle_scene method and I think that will work.
  12. Could you post your script? It's easier to see what you did wrong that way.
  13. kal

    Problem with map animations

    Try on a new project without any other scripts. If that doesn't work something is wrong with your VXA.
  14. kal

    Problem with map animations

    Are you sure about that? Paste this in the editor and do it below Sprite_Base class Sprite_Base alias update_animation_bugfix update_animation def update_animation set_animation_origin if animation? update_animation_bugfix end end
  15. kal

    Problem with map animations

    The version I'm talking about (link in the first post I made) is just copying the set_animation_origin method at the beginning of the update method. That's were I got my original version from. Still don't understand why it's so smooth in VX and choppy in Ace, but something must have changed one way or another.
×