I tried to edit it can you share a video link to where I can just edit the source code, all of it, I search online can't find a code edit video for game maker
Here's the official GameMaker tutorial series on the code editor. (I recommend using GML Code since Visual doesn't really make things easier, just prettier to look at.)
Thank God Oh I needed this so badly, I love the Pokemon engine you made, in fact I truly think it should get more coverage so many people are desprate for one & your is right here. I will definitely watch this
Total is the actual number of stat points, it gets subdivided proportionally over the stats (so the mhp-atk-def-mag-res-spd values are relative - e.g. if ATK is twice as high as DEF, the final stat will also be twice as high, but the actual value that gets stored in the end is TOTAL x ATK/(sum of all 6 stats), not ATK as-is).
The reason for why it's this convoluted is so it's easy to balance monsters - if two monsters has the same stat total, they should theoretically have the same power. (You can see this in Pokémon where there's tiers like "pseudolegendary" (600 stat total), the games never draw attention to this but competitive play communities does and that's where I got the idea originally)
With the current logic in a battle with 2v2, if you select to attack 1 monster with both of your monsters and if the first attack kills the selected monster, the attack from the next monster will 'miss' and say 'there is no target'.
Is there a simple way to update this logic so that if another monster is still alive on the enemy side it attacks that monster instead of missing? Kind of like a 'check for other alive enemies first before missing' function.
The part which applies the move is the part (starting at line 671) which begins:
//Apply to all target(s) individually
for(var c = 0; c < array_length(a_targ); c++){
Currently it doesn't do anything special to detect targets not existing, it checks if the number of hits is 0 and the number of misses is also 0 to conclude that there was no target after the fact. (Done immediately after this loop)
So what I'd do is insert a check before this loop, which checks if the original target remains, otherwise gets a random one based on the move (using the function enemies use to pick random targets):
found_targets = 0;
for(var c = 0; c < array_length(a_targ); c++){
var trg = a_targ[c];
if(battle_is_alive(trg)){
found_targets++
}
}
if(found_targets == 0){
a_targ = battle_get_random_targets(a_user, a_comm);
}
I was wondering if I could discuss a commission based on this engine? Please email me at squidbatgamesllc@gmail.com if you'd like to discuss this in more detail.
I don't really do commissions, I'm afraid - I barely have time to bring my own ideas to life :P But if you're stuck on anything with the engine, leave a question in the comments and I'll reply to it within 24 hours.
Hi Yal, thank you so much for making this engine, making my childhood dream of making a monster collector game possible! Recently I discovered a bug where when interacting with anything that opens a dialogue box (npcs, signs, chests, etc), it instantly reactivates once the dialogue is complete. The only way to 'get out' of an interaction is to hold down an arrow key that turns the player away immediately after the interaction ends. I am still a beginner at coding, so I'm sorry if this seems like a silly question. I have tried messing around with deactivation alarms and checking the interaction scripts, but nothing worked. One thing I did notice is that in the free demo version of the game there's no such issue. Could it be my GMS version? Currently, I am using the newest Version 2024.06.2.162
I tried it out and also can see it, so something has changed with GM's processing order that causes this (it was always a bug but it just didn't happen before for some reason, lol - presumably the player was always handled before the dialogue boxes but now it's handled after and thus retriggers the interaction immediately after it closes)
It's easy to fix at least, just update the "Interacting" block of obj_player's step event like so:
Hey, I've been looking to create a monster collection game, and this looks like it might be a great base to start with, but I had some questions. I'm planning on using a simplified grid battle format, so battles would take place across a 4x3 grid. Is this something that would be implementable without absolutely tearing the code base apart? There are a fair number of other changes I would be making to the traditional battle system as well, including creatures being able to potentially learn more than four moves, moves that can be powered up, and more, but I think the grid is the biggest change. Is this engine flexible enough to handle those sorts of large system changes, or would I be better off starting from scratch (or with a different engine)? Either way, this seems like a great engine, it's really cool to see something like it created to help others.
Yeah, all of the grid stuff will need to be custom-made, because there's no concept of positioning and ranges in the engine currently.
What I'm thinking will need to change:
obj_battlemonster currently just has its position as a visual thing, it will need to be able to move around... but more importantly they need to keep track of their current grid cell (because that's ultimately what matters, their x/y position is just a representation of that)
obj_battlemonstercircle is just a visual object representing the ground currently, but could probably be repurposed into grid squares; you'd give them variables for which grid cell x/y they represent and then be able to use them for reference for where to place monsters + maybe let them keep track of which cells are occupied or not. And then rather than always creating two, you'd create enough for the entire grid.
battle_get_valid_targets needs to take positioning and range into account, and in turn move data needs some new stats to store those (which would be added to init_moves/init_move).
Additionally, mev_battle_attack and bcontrolstate_ACTIONSELECT_ENEMY should be updated to do nothing if there's no valid moves to use instead of enqueueing the desperation attack (unless there's a target in range)
bcontrolstate_ACTIONSELECT_ENEMY additionally will need to select a new "move" action sometimes, e.g. when there's no targets in range. (And presumably you'll need to add a new action to the player action selection menu as well for moving, see bcontrolstate_ACTIONSELECT_PLAYER)
mev_battle_attack_select currently creates a menu to select targets when there's multiple targets and a move targets a single target; you could keep this menu but have it also discriminate by range and positioning. If you're OK with it just listing targets that should essentially be the only thing you need to do, but if you want it to highlight the grid cell you're targetting it gets a bit more involved - creating a GGUI region with no background sprite which covers the entire grid, then placing highlight regions so that they line up with individual grid cells. (Check out msh_terminal_load_page's section "Middle main: Box contents" for an example of how to make a grid menu, but the TLDR is that you want one "ggui_menu_region" for each cell, after ggui_menu_preallocate'ing a grid with as many cells as your battle grid). These cells's menu event field would be filled in with NONE if there's no valid target there, mev_battle_attack_select_target if there is.
Likewise, you'd do basically the same thing for selecting a cell to move to, except you have NONE if a cell is occupied and a new mev_battle_move_confirm_destination event if it's not + within your move range. (You'd probably want to add sprite elements to each cell which are just a red/green rectangle which represents if it's valid or not)
Currently obj_battlemonster remains even if the monster is KO'd and just is invisible, you'd probably want some extra logic to move them out of the grid when they're not replaced with backup so that a dead monster can't impede pathfinding and be selected as a valid target. Just setting their grid cells to a big negative number (like NONE) might be enough, as long as you don't try actually accessing the grid data if the position is NONE.
Hey, can I ask what you changed inside of tsprintf, for your 2.3x fix please? I had to import an older version in. This is what I currently have, thank you:
///tsprintf(format,...)
function tsprintf() {
//Trivial String Print Format
//Replaces each % percent sign with an argument, then returns the string.
//Inteded to make writing debug printouts less painful.
The change was to not modify argument0 directly, but to use a helper variable to hold it (I don't remember what the compilation error was but presumably argument is read-only after 2.3)
The current version looks like this:
///tsprintf(format,...)
function tsprintf() {
//Trivial String Print Format
//Replaces each % percent sign with an argument, then returns the string.
//Inteded to make writing debug printouts less painful.
var c, s = argument[0];
for(c = 1;c < argument_count;c++){
s = string_replace(s,"%",string(argument[c]));
}
return s;
}
There's enough differences that there's not a 1:1 mapping (partially for legal reasons). If I had to narrow it down I'd probably say "gen 3" because there's support for 2v2 battles (and horde battles) which weren't around in the GBC days.
Hey :P just wanted to confirm if it was ok to use this engine in a commercial game or not because I've seen a couple other games that are and I love how this one is set up. I'll be making everything else myself, I just would love the ability to skip the code part of it and get right into the creatives
Of course you can! It's even mentioned in the store page description. The only thing you're not allowed to do is resell it as an asset pack, anything else is fair game, commercial or otherwise.
(And just for the record you are allowed to use all of the sprites/music that comes bundled with the code in a commercial game as well, though since a lot of it are placeholder-ish I can see why you'd wanna replace them :P)
How do you make it so only the monsters that entered the battle will get exp?
How do you make in battle the move you used next time you are on the same move? So that when next turn can just press x twice in a row to do the same move again?
Both of these will need you to track some additional data, I think two global arrays with AMP_PARTYSIZE_ACTIVE slots will be enough: global.party_was_in_battle and global.party_last_move_selected.
In obj_battlecontrol's create event, fill these both with 0's in all slots.
For only earning EXP in battle:
In obj_battlecontrol's step event bcontrolstate_WIN_STEP section, change "if(amp_has_monster(exp_monster)){" to "if(amp_has_monster(exp_monster) && global.party_was_in_battle[exp_monster]){"
In obj_battlemonster's step event after the "//Monster's been seen in battle! Add to list." comment, add "if(amp_id < AMP_PARTYSIZE_ACTIVE){global.party_was_in_battle[amp_id] = true;}"
For remembering the last move selected:
In mev_battle_attack_select, right at the top add "global.party_last_move_selected[obj_battlecontrol.action_monster.amp_id] = menuvalue_x + 2*menuvalue_y"
In mev_battle_attack, at the end of the "if(validmoves > 0)" block somewhere, append a switch state like
Thanks! I got the move to remember which one it uses. I almost got the part with exp. I just it to only give exp to each individual monster based on which one they went against. How do you get for the ID of the opposing monster? So like if it's the first monster they sent out or the second.
if amp_id == 412 {
for (var i = 0; i < array_length(global.exp_to_grant_in_battle_to_each_monster); ++i) {
if global.party_was_in_battle7[i] == true {
global.exp_to_grant_in_battle_to_each_monster[i] += ceil(exploot);}}}
Code I think will work to check which ones where in the active at the right time:
if opposingMonsterID == 0 {
global.party_was_in_battle1[amp_id]
}
if opposingMonsterID == 1 {
global.party_was_in_battle2[amp_id]
}
if opposingMonsterID == 2 {
global.party_was_in_battle3[amp_id]
}
if opposingMonsterID == 3 {
global.party_was_in_battle4[amp_id]
}
if opposingMonsterID == 4 {
global.party_was_in_battle5[amp_id]
}
if opposingMonsterID == 5 {
global.party_was_in_battle6[amp_id]
}
How would I get the player monster level and the opposing monster level in obj_battlemonster: Step? Something like this?
The enemy amp_id's start at AMP_FIRST_ENEMY and then go upwards from there, until they endatAMP_FIRST_ENEMY+PARTYSIZE_MAX_ENEMY.
So the first monster that was sent out is in global.active_monster_party[AMP_FIRST_ENEMY], the second is in global.active_monster_party[AMP_FIRST_ENEMY+c], and so on.
(IDs that don't have a monster in them has an amp_MONID of NONE, otherwise amp_MONID contains the monster ID and amp_LEVEL the level)
(PARTYSIZE_MAX_ENEMY is 12 by default but if you use the constant to figure out how long arrays you need it should work out regardless)
Thanks! I got the exp for only the monster who was in the battle working.
Bug: When using a item like a potion on the overworld, if you use it on a monster and hold right or left it will scroll the items 30 times a second(You can hear it scroll like crazy).
How would I make when I use a item on the overworld the screen would stay on the monster select thing instead of closing that and going to the items menu?
The script that applies the selected item is mev_pause_items_use_actually_use, in that you'd want to remove this code at the end which destroys the current menu and the preceding one (the "use/throw away/hold" menu):
instance_destroy(daddy)
instance_destroy()
I don't see the buggy behavior in the engine source file (ggui_menu_handle also has a check that only runs the left/right handler if you've allocated a menu that's 2 at least slots wide so it shouldn't even attempt to move the cursor in the monster menu which is fully vertical) so it might be something you've changed?
Two bug reports. In a wild encounter you can mash the x button to make the wild encounter attack before you can do anything. Also when you press the escape button in a wild encounter you can mash the x button to make it press it over and over again only when the first time you escaped.
Hi Yal! Great job with this engine. Love the battle engine! i had a Q. How can i equip the Player with all the available Monsters in his party at the start of the game?
Easiest way would probably be to edit cis_intro_part2 and swap out the cc_intro_startermonsterselect / nickname logic; rather than making a monster choice menu and wait for your input, the startermonsterselect script would instead fill your party with the monsters you want:
var amp = amp_get_new_party_id();
amp_generate_monster(amp,monster_id,level)
First get the ID of the first empty slot in the party, then generate a monster there using its species and level, repeat as many times as you want. There's matching get-first-free-slot functions for the boxes and the temporary enemy slots, too.
To go through every monster like this, rather than hardcoding the monster_id you could loop from 0 to MONSTER_MAX.
Also note that if you really want the player to have every monster in the party, you probably want to increase PARTYSIZE_ACTIVE from 6.
Amazing!!! i love this engine so much. Also i tried to add a Monster. I tried to just input the code in init_monsters but do i need to also add the the "monster_WRAITHMANITA" somewhere else? What is the best way to add a monster. if you put this tutorial somewhere let me know thank you for your quick response and everything!
If you haven't found it yet, try middle-clicking constant / function names to immediately open the script file where they're created. Super useful when navigating a large project. (So in this case, you could've clicked any of the monster_ constants to go to the file where they're defined, and that'd been the place to add new ones)
Also the ctrl-shift-F and ctrl-P hotkeys for opening global search / jump-to-asset can be useful if you know something's name but can't find it.
The menu event script for this is mev_battle_escape, you'd just need to remove the special case for trainer battles.
Though you might need to take further measures to prevent the player from immediately ending up in a rematch, cc_battlestart_trainer saves the player's position after they've entered the trainer's vision range. So you'd either need to keep track of a previous position somewhere so you can deposit the player out of harm's way, or maybe give the player a temporary invincibility to further battles after they get away (and while this countdown is ongoing obj_npc_trainer's User Event 2 code - which is where they look for players - will ignore them)
what would be the best way to just remove the trainer from forcing me into a battle. I don't need that functionality in the game i'm building The player would initiate the battle by walking up and pressing the action button.
Hi Yal! me again.. I've gotten things going and I'm almost there where this works but now I can't seem to figure out how to add a room in the indoors room. There is no code in the door object and i can't find the script that gives x and y coordinates for the indoors room. The purely script based engine is very hard to get used to. I'm used to engines with more object based code than scripting. I also added another tile set for the indoors and the engine crushed it. Is the room getting resized upon output?
If you turn on the DEBUG_MODE flag you'll get some helpful printouts in the "Output" window when you load a room (check out the player's Create Event + Alarm 1 event for the code that handles where to spawn).
I think what's happening here is, you saved the game in the now-deleted room, and then when the room doesn't exist anymore the fallback code in mev_title_continue_fileselected which spawns you in the lab might not work anymore if you've changed the layout. (Or if you re-created a room with the same name but a different layout, the saved position will be used but now be in the middle of the ocean)
The door code is in obj_player's collision event with obj_door. But they're meant to be usable without adding any code, instead open the instance in the room editor and check its "variables" window:
Here you select which room to go to and a "label" (whatever text you want), the player is automatically placed at the door object with the same label as the one they entered from. So you can label doors in a town based on which character's house they lead to, and so on. (The code that loads the player based on which door they used is in obj_player's Alarm 1 event)
Not sure why the tileset is so distorted but my theory is that it might've gotten broken if it's got a different size from the existing ones? (320 x 256). Height shouldn't matter but the 320 pixel width is since the tile indices are used for collision checking (left 160px (= 10 tiles) are walkable tiles, right 160px are walls).
Also make sure the tileset settings are correct!
These should also match the grid size of the tile layer you're placing these on (A), re-selecting which tileset to use on the layer (B) will refresh how the tile layer is drawn as well.
Thank you. I figured out your label system early on. I tried to make a room in the indoor and made a door and added a new label. The player would touch the door in overworld and respawn back in the overworld. I didn't know what to do. the next thing i did was use the label of one of the already existing locations in "indoors" and altered the labels of one of the other pre existing indoors. this caused a strange confusion to occur and the tiles were crushed. I then decided perhaps it would be best to make a new room. I copied the room to ensire if there were any game code it would remain intact but upon doing so i destroyed the game. I deleted the room and then repawned in the ocean. I just checked the settings on the tiles and they are correct and also correct in the room. As you are correct the code must be sending me to the ocean of a room that doens't exist however, it has the same sound as the forrest so i think that's where its sending me. How can i fix? I may also try adding the tiles in want to the pre existing tile set provided to see if that works.
What I was thinking was, you'd enable DEBUG_MODE (line 5 of init_constants is "#macro DEBUG_MODE false", change it to "#macro DEBUG_MODE true") and then you'll get messages telling you what happens. E.g. if a door transition takes priority, the game will print "Came through a door, jump to LABEL" (so if you're not where you think you are, you can check that the label is correct) and it'll tell you "Player loaded! (room=NAME OF ROOM)" as well so you can tell where you ended up.
You could try adding additional show_debug_message's to e.g. print the player's position after loading, that should make it easier to find in the room editor:
As I said before, going through a door will place you at the door with the same name in the other room. So a drawback of this approach is that you can't link two doors in the same room together (because when looping over the door objects it'll find whichever of the two doors with that label is first in the instance list and always pick that). If you want to warp between places in the same room I'd probably create two new objects, "obj_teleport_entrance" and "obj_teleport_exit", which has a label the same way the doors have. Then in player's collision with teleport_entrance, run this code:
Now each teleport pair would use one entrance and one exit. If you want a teleport to be in both directions you'd have entrance A and exit B on one side and entrance B and exit A on the other side; make sure to place the exit some distance away from the entrance (otherwise the player gets stuck in an infinite loop of warping between them).
Hi Yal, hope you had a great Summer. I've been trying to setup a 'shop' that displays the monsters seen, and purchase/craft monsters from a list that of them you've caught.
I've taken your UI of mev_pause_moncyclopedia for the shop layout, and am attempting to use the item purchasing goodness from msh_spawn_shop_list and its menu event accomplish this.
The thing I think I'm currently stuck on is using the proper code to set up the var it which I want to be the specific ID of the monster you're hovering over .Then carrying over that info with shop_item variable to the shop menu event.
I've been using it = global.monsters_seen[d] but after starting to use the debug feature, I realized this is not giving me what I need lol.
Here's the code block I'm struggling with to accomplish that. Its the block for the mon's you've caught and thus should be purchasable. I'll link the full script code in a comment below if that helps. Thanks!!!
//Add meta data as well, based on how much info the player has about the monster.
global.monsters_caught and global.monsters_seen are arrays of booleans, which means each slot is true if you have caught/seen that monster and false otherwise. In this case you'd just want to carry over the counter itself, since it's the monster ID - so menuvalue_y for the currently hovered item (since it's a vertical menu).
Also, in the moncyclopedia when setting up the events (ggui_menu_region statement) you'll see we give every line a NONE event (which means nothing happens if you press the "accept" button with it highlighted), for your case you'd maybe want to make this conditional - if the monster is available for purchase (global.monsters_caught[d] is true) you'll set up a shop event (which e.g. checks if you can afford the monster and if so proceeds, carrying over the current menu's menuvalue_y as the monster ID the next-level menu refers to), if it's not available (e.g. you haven't caught it yet) you create a NONE event instead.
Thanks Yal! That explains why the monster that keeps getting purchased is Charchne, who is ID no. 1 haha. Could you expand on how I can use these boolean arrays to carry over a counter to properly match the currently hovered mon?
To pass over a value, in the mev_ script you'd do something like this:
with(instance_create_depth(x,y,depth-1,obj_gguimenu)){
daddy = other.id
my_monster = other.menuvalue_y
//Create frames, elements and events like normal
}
(Similar to the scripts for doing stuff to party monsters, but instead of referring to the AMP we refer to a raw monster ID)
Could you help me in the game gun 10 summer, because I have trouble finding a weapon to break the green and blue blocks. I'm currently in Western Flat, after entering the room downstairs where I'm talking to someone there, I can't go back upstairs because the shotgun isn't enough. When, after the fight, the firewall "A" opens, which I go through, and after entering the room, the computer asks for an ID that I don't have and I don't know where to get it. In this room, my shotgun is also not enough to get to the top
Could you help me in the game gun 10 summer, because I have trouble finding a weapon to break the green and blue blocks. I'm currently in Western Flat, after entering the room downstairs where I'm talking to someone there, I can't go back upstairs because the shotgun isn't enough. When, after the fight, the firewall "A" opens, which I go through, and after entering the room, the computer asks for an ID that I don't have and I don't know where to get it. In this room, my shotgun is also not enough to get to the top
Hello again Yal! Do you know if this engine works OK with the most current versions of GM that have come out? I mainly ask because I know there are some retired functions (like arraysize 1D) used. I haven't updated it in forever just in case haha
also a general GM question i'm trying to figure out, I've noticed you use var c for most of you "for" loops. Does that 'c' value get carried over and iterated on in scripts that use multiple 'fors' or is the 'c' reset upon a new 'for'?
I tested most of my engines with 2023.8 to make sure nothing had broken, I only found a single issue (a missing sprite_exists check in the open world 3D engine). array_length_1d is deprecated but trivial to replace (array_length does the exact same thing) so I figured there's no rush, there's currently no easy way to find a list of all deprecated functions in use in a project so I'd probably just miss something, think the project is safe and then run into a massive tech support nightmare when a new GM update drops anyway.
As for the for loop variable name, I'm just using "c" because there's a programming language called "C++" so writing "c++" as the increment operation is funny. That's the whole reason :P
Anything declared with "var" only exists in the piece of code it belongs to, so c's in different scripts will not affect each other. And with multiple loops in the same script, you reset the variable to 0 at the start of the for loop, so it doesn't matter it's reused. (The exception being when you have a loop inside a loop - then you need separate variables so the inner loop won't mess with the outer loop).
Hey Yal, I picked the engine up a couple days ago and I love it, I have 2 questions and I was wondering if I could get some help or advice.
1. I've redone the move points system to be a pool of points that each move pulls from. I was wondering is there anyway to check to see which side used the attack so the enemy doesn't use the pool of points as well?
2. How does the enemy attack selection work? It looks like it puts all the attacks into a list and then shuffles them around and picks the first one. I was wanting to change it so it's choices are weighted based on mp cost/turn count, so higher mp moves are selected as the fight goes on.
1: There's a 'user' field for enqueued actions, for instance the a_user variable in the ctst_TURNQUEUE state in obj_battlecontrol's step event. (this state is probably when you want to consume the points, since it's when the action actually happens). This "user" field is a battlemonster object ID so simply check its side variable: if it's equal to side_ENEMY it's an enemy and shouldn't consume points.
2: Correct, enemies just pick a random move this way (add all valid actions to a list, shuffle it, pick the first option). If you want something priority-based you could rework what happens in the bcontrolstate_ACTIONSELECT_ENEMY segment to use a priority queue instead (ds_priority functions), where you insert things and they're automatically sorted so you can pick the highest priority value - it's pretty much analoguous to how ds_lists work and the only hard part is figuring out how to compute the priorities - you might want to add some new fields to the move database for storing base priority and priority increase per turn to make this easier to control on a case-by-case basis, and maybe rework target selection a bit to account for type matchups (i.e., add every possible move-target combination to the list but multiply the regular priority with the type multiplier for that target)
(Also you might want to still add a little bit of randomness to the priorities, so the AI doesn't become completely predictable and the player can exploit it)
I've also found a bug whereby the game will crash if you try to catch a monster that has already been captured in the same turn, because there is no longer a target. So two player party members, on both party members turn you use a monster ball item, if on the first party members turn the monster is caught, the second party members turn tries to use the monster ball but they can't be captured again, causing a crash. I fixed this by checking in the battle_control before using an item, if the item is a catching item type if the target is still 'alive', and if not, tell the player that it failed etc. If you can replicate might be worth fixing for everyone etc
Oh yeah, might need to have a look into that. I know even the released version of Pokémon Colosseum had a bug which let you duplicate items if you used an item as the first party member and then reordered items on the second party member's turn (so you could get infinite Master Balls that way) so it seems this kind of stuff is tricky to get right.
Hello, another question, how would I go about making it so that the 'box' management makes it so you have to keep two monsters in your party instead of just one? I'm focusing exclusively on double battles and don't want the player to have only one monster in their party at any point
and now emptying either of the first two slots in the party (by going below 2 party members) should cancel the operation.
(Also, you probably want to update the message that is printed when this happens to read "2 monsters" instead of "1 monster", a little further down in the script)
I just realized, doing it this way still will cause some issues since grabbing a party monster will move any monsters after it in the party lineup forwards one slot, and the undo overwrites the original slot (so grabbing the first monster when you have only 2 in the party will move the 2nd monster to the 1st slot, then it's overwritten by the undo).
It's probably safer to move the party-amount check to just after line 18 (inside the second "if(menuvalue_x < columns_party){" block), and have the check be something more like this:
var partymons = 0;
for(c = 0; c < PARTYSIZE_ACTIVE; c++){
if(amp_read_var(AMP_FIRST_ACTIVE + c,amp_MONID) != NONE){
partymons++;
}
}
if(partymons <= 2){
//Error message code goes here (but without the undo, since we don't always pick up the monster now)
}
else{
//Regular "pick monster up" code goes here
}
So basically, disallow even picking up the monster if you don't have at least 2 after the operation.
Thank you. If I'm understanding correctly, this will also mean that the player can no longer 'swap' a monster in the party for one in the box if they have only 2 in the party meaning they will first have to get the monster they want from the box and add it as the 3rd party member, then put the one from the party into the box they don't want, correct?
I actually went about this a different way in the end. I modified the obj_terminalmenu step event so that you can't cancel out of the menu if the party has less than two members and put a warning if so. I remove the 1 party member warning from the grab so that you can freely move monsters however the player wants. I can't think of a reason why this wouldn't work, can you?
This sounds like it should work as well, it doesn't matter if you enter an invalid state as long as you're forced to revert to a valid state before you can close the menu. Just be careful about implementing autosaving, new ways to cancel the menus (etc) so the player can't end up stuck with too few monsters that way.
Yes for sure, I realised as well that there is not a release function in the box menu, at least, not one I can see??? So if that's the case and I implement one, I'll just need make sure that the party + box monsters is greater than 2 before you release a monster 😌. But anyway, big thanks for your help and the engine, has been a little tricky to get my head around some things but its progressing nicely
Hello again Yal! Hope you're having a good summer,
I've been working on a shield system by copying how HP works and changing all the variables to ShieldHP essentially. I'm having trouble figuring out how to get the damage to "jump" over to reducing normal HP after the shield bar has been destroyed. ( I have separate dmgapply scripts for shield dmg and normal dmg below) It seems like the damage is stuck on trying to take from the sheildHP pool even after it has reached zero. Probably some simple thing i've overlooked :) Here's what I'm working with:
//Damage
if(pow > 0){
var dmgdata = compute_damage(a_user,trg,a_comm);
if(sheildcheck >0){
n = instance_create_depth(trg.x,trg.y,depth,obj_dmgapplyshld)
n.my_dmg = ceil(dmgdata[0]*ail_power_multiplier)
n.my_mul = dmgdata[1]
n.my_eff = dmgdata[2]
n.my_mon = trg
n.my_user = a_user
n.my_move = a_comm
n.this_is_an_attack = true
}
//********************
if(sheildcheck < 1){
n = instance_create_depth(trg.x,trg.y,depth,obj_damageapply)
n.my_dmg = ceil(dmgdata[0]*ail_power_multiplier)
n.my_mul = dmgdata[1]
n.my_eff = dmgdata[2]
n.my_mon = trg
n.my_user = a_user
n.my_move = a_comm
n.this_is_an_attack = true
//User might hold an item that improves attack effectiveness
The code you posted seems to make sense. The interesting thing here is how you compute sheildcheck, and you didn't include that bit of code...
Also, just to get the obvious out of the way: obj_damageapply still does HP damage and not shield damage, right? (So you didn't accidentally change that one as well when you changed all the variables)
(Also note that your two sheildcheck checks are >0 and <1 so both of them will occur for values between zero and one! Is this intentional?)
trg here is the target monster object and we've already checked that it's alive, so you really only need to check if amp_read_var(trg.amp_id,amp_SHIELDHP) is greater than zero: if so, deal shield damage, else deal regular damage.
I'm trying to make it so that when the cursor is over a move in battle, where the box that shows the type of the move and it's MP, I can add in a sprite to indicate the type. I'm looking in the attached image but I'm not sure what element to use to get it to draw the correct icon (I already have the move data setup with the type icons, I just need to know how to draw it)
gguielt_CURRSPRITE element type (added with ggui_element_sprite_current script) is the icon sprite of the currently hovered item in the menu, this is what you want to use here.
Since the data is filled into the menu using ggui_menu_add_option_text it fills the sprite data with NONE, but you could copy ggui_menu_add_option_text and create a new script ggui_menu_add_option_text_and_sprite that also fills the sprite/image data (the two arguments to ggui_fill_meta that are set to NONE within ggui_menu_add_option_text) using two new arguments at the end, and then use that instead in mev_battle_attack. (There's already the _icontext and _gridimage helper scripts that fills the sprite data, but they also set up the elements so that the image is used directly there in the body of the menu, which isn't what you want for this - we want this new script that fills the icon data but doesn't use it yet)
Hello again Yal, happy July :) Here's some things I've been having trouble understanding lately:
How to use properly reference a AMP (friend and foe) within obj_battlecontrol. I'm working on shield system, (where mons have to break through a shield before being able to attack the enemies HP)
I'm trying to do a check on a new stat (shieldHP) and if the AMP's shieldHP stat is >1, a visual shield object will appear in front of a mon after it's summoned.
My problem is simply trying to understand how you have been using the amp.id variable in obj_battlecontrol do to similar stat checks. I've tried a few things and placing the code in a few spots. I don't think I quite get what variable to place in front of it (with the period separating amp_id.
Here's the current code I'm trying in bcontrolstate_ACTIONSELECT_STEP 1 (around line 320)
var getshld = monster_get_maxshld_hp(action_monster.amp_id);
I think you're going about this the wrong way - if you want a visual effect on a per-monster basis it might be easier to have obj_battlemonster handle it? It's already aware of which AMP data block it owns so it's a bit easier than going through the control (who manages a ton of monsters at once) Plus, with your current approach you'll create a new shield effect object every turn - if you don't remove them afterwards you'll end up with dozens or hundreds of them in every battle...
In the draw event, add some new code like this:
if(state == bmonsterstate_NORMAL){ //Monster is guaranteed to be alive and valid in this state
if(global.active_monster_party[amp_id,amp_SHIELDHP] > 0){
draw_sprite_ext(spr_shield,0,x+dx,y+dy,image_xscale*drawscale,image_yscale,image_angle,image_blend,image_alpha)
}
}
There's ways to refine it, like adding effects whenever the shield status changes (broken / regenerated) by keeping track of the status the previous step and doing stuff when it's different from the current step. But the important part for now is making it draw properly.
Hi Yal, any idea why I'm getting errors in global.active_monster_party? I've been so tied up in working on totems and menus, when I went back to look at battles, this happened :( Something to do with the Macro NONE i'm sure, but I cant figure out what.
ERROR in
action number 1
of Step Event0
for object obj_battlecontrol:
Push :: Execution Error - Variable Index [-12341] out of range [419] - -5.active_monster_party(100075,-12341)
at gml_Object_obj_battlecontrol_Step_0 (line 110) - var montype = global.active_monster_party[n.amp_id,amp_MONID];
The thing that's happening here is that you try to read the data of a monster that has a NONE AMP reference. It's specifically being done in the block that creates the enemy battlemonster objects.
From what I can see, the issue happens when you start a horde battle without generating enough monsters to fill all enemy horde slots. (This is why it works in 1v1 battles - you always generate 1 enemy monster so creating 1 battlemonster will avoid reading outside the available data)
Should be simple to solve, at least. Wrap this block
in a copy of this condition
and it shouldn't try to read the unset data anymore. (Note how the inner block also changes the monster state from "DEAD" to "NORMAL" when doing this, which will also lead to issues down the line even if you ignore the data reading bit - this is what causes the second crash)
Actually, the reverse is working (horde battles working, 1v1 battles are not) but I tried adding that condition but then got a similar error to what I have been getting:
##############################################
ERROR in
action number 1
of Draw Event
for object obj_battlemonsterhud:
Push :: Execution Error - Variable Index [-12341] out of range [419] - -5.active_monster_party(100075,-12341)
at gml_Script_amp_read_var (line 3) - return global.active_monster_party[argument0,argument1];
##############################################
gml_Script_amp_read_var (line 3)
gml_Script_monster_get_name (line 4) - var s = amp_read_var(amp_id,amp_NICKNAME);
Either way it's still caused by there being NONE monster IDs in the list of enemies to fight. Weird.
I had a little dig in the codebase and I think I have another idea for what could cause this. The code that checks for valid monsters to send out only checks if HP > 0. If the matching AMP ID's amp_MONID is NONE then we have an invalid monster ID and reading data from that will cause issues.
Do you set up / clear data for totems properly (similar to how the engine handles enemy data in random encounters)? It feels like there might be something going on with partially initialized data that causes this.
One idea might be to extend battle_get_valid_reinforcements a bit so that line 6 checks this condition instead:
It's not fixing the core issue that causes the invalid data but it at least stops trying to spawn a nonexistant monster. Maybe it'll be enough, since most other AMP manipulation scripts checks if the monster ID is NONE to check if a slot is valid.
Hello again Yal! This time i'm back with more general questions that I can't seem to figure out yet.
I'm using the framework of msh_spawn_inventory and how it conveniently lets players tab through multiple pages. The way i'm trying to use it, I need certain items to be different font colors but I cant seem to figure out how as this uses ggui_fill_arbtext. any advice appreciated :)
Also, could you give tips on how we might be able to display animated sprites within the menus/cutscenes. They all seem static from what I can tell.
Let's start with the easy bit, animated sprites. Just create a GGUI sprite element with the subimage set to -1, and it should work (draw_sprite uses the current animation subimage of the object if you draw a sprite with subimage -1, and sprite elements are drawn with draw_sprite passing through all parameters as-is). Potentially you might need to give the menu object an animated sprite as well (by assigning sprite_index) but I think it'll work even without it.
Now for "certain items with a different color": It's a bit unclear what you mean by "items" here? Items in the inventory or GGUI elements?
The idea with (Scrollable) Arbitrary Text elements is that you have a text field that reads data from the currently highlighted menu item rather than being a preset text (they're "arbitrary" as opposed to the Description field which originally was the only per-element-changeable text, and hardcoded text strings - I added the arbitrary text fields because I realized sometimes you need more than one per-element text field).
So basically, the element itself has no text, just a reference to the Arbitrary Text Array for that menu. But the Arbitrary Text Element owns the formatting (font, color, alignment) so by default, it can't change depending on what's highlighted - you'll need to make a new type of element which accesses an array of both text and formatting information (studying how gguielt_ARBTEXT works internally and copying the behavior for a new gguielt_ARBTEXTWITHFORMATTING might help here - you'd use an array similar to menu_arbtxt but rather than just storing text, you'd store a tuple [text, font, color, halign, valign]).
But if you just want different fields to be different colors (e.g. HP is red, money is yellow) it's very simple, use ggui_element_text_settings to change formatting parameters (all further text elements are created using these settings until you change them again).
Hmm, I guess this is a side effect of the menu not having a sprite, so it doesn't use the sprite's animation speed... welp, it's easy to fix at least.
Set image_speed to a value lesser than 1 from the menu object's perspective (i.e. inside the block that starts with the "with(instance_create_depth(...obj_gguimenu)" call).
I find values between 0.1 and 0.2 work the best for legacy sprites that default to 60 fps, so start off with 0.15 and adjust as needed until you're happy with the result?
Hi Yal! Hope you've been well. Any ideas on the best way to implement a "totem" system?
Basically, certain moves create totems - essentially a temporary, targetable object that gives buffs, etc until they are destroyed.
I thought the best way would be to add them to the Dex .Then have them sent out, then destroy the instance when they reach 0 HP or at the end of battle.
I'm not sure how to approach adding a temporary mons to the battlefield (also considering the opposing team could use this move!) I'm thinking the move would have to temporarily increase the max team size to work.
Good job sticking with the project for 6 months! I'm used to people giving up much sooner but seems you're one of those rare determined people that actually finishes their games ^__^
Your analysis is pretty much correct!
Adding temporary mons would be pretty easy, as you can see in obj_battlecontrol's step event ctst_ACTIONSELECT block, it loops over all existing monster objects when building the list of who gets to act, so just creating instances should work. You should add a new "is_totem" variable to the battlemonster object so that totems are treated differently, though (it's always false, but when creating a new instance for a totem, you'll set it to true after creating it).
You might wanna do some changes in obj_battlemonster's bmonsterstate_DIE to change the KO messages for totems (and delete the object if dead - an empty slot currently still is a monster object!) so they're not around when the ctst_REINFORCEMENTS step happens, or that team will be able to send in additional monsters to take the place of any destroyed totems, turning it into a permanent team size upgrade for that battle.
You will also need to change this block:
default:
//Most ailments don't bypass action input, so let's go do that.
if(next.side == side_PLAYER){
substate = bcontrolstate_ACTIONSELECT_PLAYER
}
else{
substate = bcontrolstate_ACTIONSELECT_ENEMY
}
break
Totems should always go to bcontrolstate_ACTIONSELECT_ENEMY (automatically select), regardless of if they're player or enemy. (Unless you want players to be able to control what their totems do, of course - I'm just interpreting your description as totems being kinda passive/automatic)
Their buffs could be implemented as new moves with the appropriate target range set up (e.g. movetarg_ALLALLY to buff all allies), then you have the new totem species learn the buff-moves it should provide at level NONE (i.e., always knows them).
Since all monster stats are read from the AMP and the battlemonster objects just have a pointer to that (rather than having their own stat variables), I'd say you could put totem data in the "enemy" section of the AMP (since that's temporary data which gets cleared after a battle) - just run amp_get_new_enemy_id / amp_generate_monster using the totem species for this. You just need to manually free up slots again (with amp_clear_monster) when a totem is destroyed so you don't run out of slots entirely if a long battle has many totems created and destroyed.
Thanks so much Yal! I am still familiarizing myself with the engine, but as i've taken your above advice into action, I realize I may be in over my head with a this rather complicated totem mechanic and may need to revisit it later. BUT if you have time, these are a few things I'm running into:
- Not sure where in the init moves to put amp_generate_monster(1,monster_totem_1,1)
I've tried placing it in a few of the argument fields there with no success. Maybe I need to create a "status" with 100% acc and code it to create totems that way?
- I have tested forcing a mon to appear in battle by just doing a press key event in obj_battle_control. I can't get it to pop in a duo battle (with one mon on my team) but it will pop into my party (and can be swapped into lol) I wonder if creating a designated slot for totems might be the way to go, unless I'm missing something which is likely lol.
The easiest way to do really weird things that doesn't fit into the standard move data would be to hijack the "aniobject" argument (mvd_ANIMATIONOBJECT slot of move data). It's intended to be used just for visual effect controllers, but nothing's stopping you from creating an object which in turn sets up a totem, or does other weird stuff (a boss could have a move that plays a cutscene, for instance). The only thing to keep in mind is that it needs to be a child of parent_battleeffect if you want the battle to pause until the object is destroyed (i.e. it's gonna be around for more than one frame). Do the behavior you want to achieve in the step event, since we set some variables after creating it (and thus they're not accessible in the create event).
Also the code you're running is probably not doing what you think you're doing...
The function is amp_generate_monster(slot,species,level). First argument is slot, if you just put an "1" there it's always going to overwrite whatever's in the second party slot (array numbering starts at 0 in Game Maker). You want to ask the engine for an unoccupied slot, so you probably want
var slot = amp_get_new_enemy_id();
amp_generate_monster(slot, monster_totem_1, 1)
var totem = instance_create_depth(xx,yy,dd,obj_battlemonster)
totem.amp_id = slot
totem.name = monster_get_name(totem.amp_id)
totem.sprite_index = monster_get_battlesprite(totem.amp_id)
xx, yy and dd are the X, Y and depth values to create the totem on, you'd have to compute them somehow. For this you could use the fact that effect object you created has a variable "user" set by the battle controller, which is the ID of whoever ran this move, so if you e.g. want the totem to spawn in front of the user, you'd do something like:
(potentially you might also wanna use the "target" variable which is the move's target, e.g. if you could create a totem that affects someone else in a multibattle)
Hi Yal thanks for everything :) Its a great engine. I was wondering what I could do to implement mouse controls? When I hover my mouse over the game window, the cursor disappears. Thanks again
Let's start with the easy part, how to enable the mouse cursor. Open the Game Options, then Windows, then the Graphics tab and check this box:
(if you want to draw your own cursor you could draw a sprite at the built-in variables mouse_x, mouse_y - each of the control objects could do this)
Having actual mouse control is considerably more complicated but I think something like this would be the easiest way:
Edit ggui_menu_handle to comment out all the keyboard controls
Add a new loop that goes through all frames and does something like this:
if(active){
for(c = 0; c < ggui_frames;c++){
if(mouse_x > ggui_frame_l[c] && mouse_x < ggui_frame_l[c] + ggui_frame_w[ c]){
if(mouse_y > ggui_frame_t[c] && mouse_x < ggui_frame_t[c] + ggui_frame_h[ c]){
//We're in this frame, select it
for(var d = 0; d < menu_w; d++){
for(var e = 0; e < menu_h; e++){
if(menu_frame[d,e] == c){
menuvalue_x = d; menuvalue_y = e;
if(mouse_check_button_pressed(mb_left)){
//Clicked something! Run the regular "A button" code
if(menu_event[menuvalue_x,menuvalue_y] != NONE){
sfx(snd_menu_ok)
script_execute(menu_event[menuvalue_x,menuvalue_y])
}
else{
sfx(snd_menu_buzzer)
}
}
break; //End when we find the first frame that matches
}
}
}
}
}
}
}
Now this will remove the "cancel button" option you had before but you could solve that by adding a "X" button in the top right corner. (You still need to preallocate enough menu options for all buttons but since the player needs to physically hover them you could put any "padding" buttons offscreen if you need to allocate more buttons than you'd physically use)
(actually maybe it would be easier to handle the X button out of GGUI entirely - just draw a "close" button at the top-right of every menu frame and check if you're in the top right 16x16 pixels of the menu in the Mouse Left Released event, if so destroy the instance and don't even check for frames)
There's more stuff you could do here, like playing the "menu move" sound when you detect that you changed either menuvalue_x or menuvalue_y, but this should be a good starting point.
Thank you so much Yal! I can't believe the thoroughness and speed of your replies :) This is a great start for me, I'm very new, and still making sense of your scripts/variables in my head. I'll be sure to implement this mouse implementation once I get a better grasp of the overall engine.
Hello again. I've been trying to get mouse controls to work. I managed to get the mouse buttons working no problem, but I'm having a fair bit of trouble getting the menu to figure out which menuvalue_x and y the mouse should be on. I tried the code above, and it only seemed to partially work. On the main menu it only every selects New Game. On the player name select it goes to 'M' immediately, then if you click it selects all the letters from A-L.
I also tried a different system where I defined some variables and used them to figure out the menuvalue_x and menuvalue_y. The variables I used:
var mouse_y_gui = device_mouse_y_to_gui(0);
var mouse_x_gui = device_mouse_x_to_gui(0);
var array_l = array_length(ggui_frame_l) -1;
var array_w = array_length(ggui_frame_w) -1;
var array_t = array_length(ggui_frame_t) -1;
var array_h = array_length(ggui_frame_h) -1;
var menu_width = ggui_frame_l[array_l] + ggui_frame_w[array_w];
var menu_height = ggui_frame_t[array_t] + ggui_frame_h[array_h];
var menu_x = ggui_frame_l[0];
var menu_y = ggui_frame_t[0];
First of all these give you the wrong sizes, just using the frame sizes should be enough (don't add the left/top position to the width/height)
var menu_width = ggui_frame_l[array_l] + ggui_frame_w[array_w];
var menu_height = ggui_frame_t[array_t] + ggui_frame_h[array_h];
I also don't see why you divide the pixel size with the number of elements along the axis, they represent the same ratio if evenly spaced and that means translating between them should "just work". Though you'll need to make sure the output stays in range:
(And also note that "If evenly spaced" might cause issues with the more esoteric layouts since nothing forces a menu to actually be evenly spaced, you can place frames anywhere and fill them with whatever you want)
Did you check the code in ggui_draw that draws the rectangle over the currently selected item? It converts from grid coordinates to screen pixel coordinates so it might help a bit here. You could even skip having to do the reverse computation (grid coordinates from screen coordinates) if you loop over all menu options, compute their corresponding frame coordinates (using this code), and then use point_in_rectangle to check if the mouse cursor is over that frame. Slow, but 100% accurate.
(Note that drawmenux/drawmenuy, the scroll offset, are computed at the beginning of the function)
I got it to work! First, thank you so much for you help Yal! Your help has cleared up a bunch and made it so I could figure this out. It took a bit to try and understand how the menus work, and I still don't get it all, but for those interested in getting the mouse to work, here's what I did:
Then I found pretty much everywhere that k_a was and made it (k_a||k_ma). And the same thing with k_b, I changed them to (k_b||k_mb).
This was to allow for the mouse button to work as the 'A' or the 'B' button, while still keeping the regular 'A' and 'B' button working. So you can use either the mouse or the keyboard.
The part that I had trouble with was figuring out which menu box the mouse was in. What I ended up doing was looping through the options to see if the mouse was hovering over an option. Than looping through the options and checking if the mouse was in each rectangle until it found the right one. It's on the more expensive end, it requires a few loops, but it does seem to work, and I haven't noticed any issues.
So, here's what I put, right near the end of ggui_menu_handler (just above the closing }):
for(c = 0; c < ggui_frames;c++){
//check to see if the mouse is hovering over an option
if(mouse_x > ggui_frame_l[c] && mouse_x < ggui_frame_l[c] + ggui_frame_w[c]){
if(mouse_y > ggui_frame_t[c] && mouse_y < ggui_frame_t[c] + ggui_frame_h[c]){
//the mouse is hovering over an option, so loop through the options to find out which one we are over
for(var d = 0; d < menu_w; d++){
for(var e = 0; e < menu_h; e++){
frame = menu_frame[d, e];
//here we are checking to see if the mouse overlaps with the rectangle created by the menu option
if point_in_rectangle(
mouse_x,
mouse_y,
lerp(ggui_frame_l[frame],ggui_frame_l[frame] + ggui_frame_w[frame],menu_reg_l[d, e]),
lerp(ggui_frame_t[frame],ggui_frame_t[frame] + ggui_frame_h[frame],menu_reg_t[d, e]),
lerp(ggui_frame_l[frame],ggui_frame_l[frame] + ggui_frame_w[frame],menu_reg_r[d, e]),
lerp(ggui_frame_t[frame],ggui_frame_t[frame] + ggui_frame_h[frame],menu_reg_b[d, e])
)
{
menuvalue_x = d;
menuvalue_y = e;
break;//End when we find the first option that matches
}
}
}
}
}
}
If anyone has a way to refine this and make it less slow and costly, I'd love to hear it. If what I have put above is unclear, let me know. But, this is what I did, and the mouse seems to be working well.
Hello, question! I want to change the height and width to 1200 x 800 ( or full screen ) so i can play on a bigger screen. Now is my question where i have to start to make it work? I know i can change the height and width of the room, but which another things i have to change?
The majority of GUI elements are based on the VIEW_W and VIEW_H macros, update those to match the desired screen size and a lot of things should "just work". (Battle elements and some misc stuff use the room size for positioning instead so make sure to update all "1x1 screen"-sized rooms like battle, evolution etc to match the new size). Also on overworld rooms, the viewport size + camera follow border size needs to be updated, since views are set up in the Room Editor instead of by code and they're all hardcoded to 640x360 / 320x180 right now.
You might need to make some sprites, fonts and other graphics elements bigger as well, since they might look weirdly tiny on a much larger screen (and you also change from a 16:9 to a 4:3 aspect ratio which might squish some things horizontally) but that's an optional UX thing and not something that's technically necessary for this to work.
Right now interactions are handled in obj_player's step event, lines 81-98 (the block starts with a comment that reads "//Interacting"). It's a basic collision_circle check so there's two ways you could solve it:
Hacky way: make the object alter its collision mask depending on how the player is facing, so it has zero collision-able pixels when they're facing the wrong way.
More stable way: have the interact_script of that object check the player's facing, and simply do nothing if they're facing the wrong way (so you trigger interactions but they only start cutscenes/etc if the player is facing the right way). You might need to make a custom object for these directional NPCs to have an easier time overriding the regular behavior.
2) Is the easiest... since you've got access to the user's object instance you could add two local variables to obj_battlemonster, the last move used and how many times it has been used in a row; the times-in-a-row counter would be incremented by 1 if you use the same move as last time, otherwise reset. (You might also wanna reset it on switching, if you use an item, fail to act due to ailments, etc). You'd add a new "stronger if used in succession" flag to the move data and compute_damage would take the times-in-a-row counter into account if the move has the flag set.
1) Probably also is easier with new movedata fields; min/max number of hits. (Two separate fields if you want to allow moves to have a random number of hits - then you'd pick a random number between these with irandom_range. Moves that always hit the same number of times would have the min and max values be the same)
Where to insert this is a bit trickier, you could slate multiple actions but that duplicates messages and would run ailment checks multiple times. Another idea could be to make a separate "multi-hit deployer" object which is spawned after the regular damage/side effects block when the current attack has multiple hits; it would be a child of parent_battleturn so the turn doesn't automatically proceed, and would have a copy of the user/target data, the index of the move being used, and a counter of how many more hits it should cause. Whenever there's only one parent_battleturn left (remember, we're one too so the counter won't hit zero), it would run a copy of the damage/side effects code (ideally you might wanna break that out into a script to make it easier to keep adding new things in the future rather than actually duplicating it in two places ?) using the user/targets/move data it got a copy of, decrementing its hit counter each time, and if all targets are dead or the hit counter reaches 0, it destroys itself.
Hey, thank you so much for this engine:) I am learning a lot from it, all your stuff here on itch.io looks totally awesome! I also read through all the questions here to learn even more about the engine!
Also, as an advice for other mac users considering buying this: This code worked fine on my mac, but to get it running you have to open and save it at least once in windows. Thats at least how it was for me!
For now, I have two questions:
1: I would like to implement a minigame when the enemy attacks, kind of like Undertale. Depending on how many times the opponent hits during the minigame it would cause the player damage. Is there a way to implement something like this?
2: Next to four attack moves I would like to also give the player four dialog options, giving the player the opportunity to either fight or talk their way to success during battle. Any Idea how I could implement this in an easy way?
Intriguing; I know GM is a bit weird with cross-compiling setups but I've never heard of anything like that. I guess some project paths won't get updated automatically unless you save on Windows and it was still using the ones from my computer...?
Anyway, onto your questions:
1) The attack is applied inside the loop in obj_battlecontrol's step event at line 672 (after the "//Apply to all target(s) individually" comment). The variable trg is set to each target individually; for your use-case I guess you would want to check whether that is a player or enemy character and do different things (it's the ID of a battlemonster object so just check its side variable) - for enemies you can keep the current system, but for players you'd branch out and create a minigame object instead, and it is responsible for applying damage, side effects etc depending on the minigame result. If it's a child of parent_battleturn the RPG turn flow should automatically wait until it destroys itself with no further action from your side.
(Basically add an if statement that checks if trg.side == side_PLAYER, if so create the minigame; then put the entire existing code into the else block)
If you want different minigames for different attacks, the easiest would probably be to extend the move data (see init_moves / init_move) and add a new field for "minigame spawn script" which is run when the move is performed, creating the appropriate minigame object(s) and setting parameters for damage etc.
2) I'm thinking the easiest way would be to give each enemy monster species an array of dialogue-options / script pairs, and then you start a cutscene where you immediately deploy a cc_question that uses it - basically what cis_shrine does, but you'd use a monster-species-specific variable for the menu alternatives rather than a hardcoded array; each of the scripts that are run when you pick an alternative would either display dialogue, enqueue extra attacks, or alter a "progress" variable in the affected enemy (or whatever else you want the dialogue system to be able to do)
For this system to work, there's a couple things you need to do:
There's two places where obj_battlecontrol's step event checks that there are no parent_battleturn instances; those checks need to be extended to also check that there's no obj_cutscenecontrol.
The monster data needs to be extended with a new field, the array of their dialogue options. You'd just store this as-is. (Game Maker didn't support arrays inside arrays when I made this engine so things like moves are broken up in the engine right now, but that's not a limitation anymore)
You'd need to add the "talk" option to the turn menu, its setup code's currently located in obj_battlecontrol's step event on line 359.
Figuring out who to talk to - so you can load their dialogue data into that dynamic cutscene - might require you to select a target, check out the code in mev_battle_attack_select for an example of how to do this.
When viewing a monsters status from the menu, is there any way to make it so that if you press say the up / down or left / right keys, it'll change to the next monster instead of having to back out each time and select a new monster?
It's gonna be a bit complicated but I think this should work:
in mev_pause_monsters_status, assign a new script to the script_step variable of the new menu that is created.
This new script should check for left/right presses and if so do some stuff; we'll get to that in a little bit. Note that the menu objects already get_keys() every step so you only need to check the input status variables - if(k_l && !p_l) to check for a left press, etc.
In mev_pause_monsters_status there's a giant block of code that creates the entire status menu. Break that out (everything inside the with loop) into a new script which takes two arguments monsamp and monid, these are the variables we use to read the selected monster's data - changing them to script arguments lets us reuse this blob of code without having to change it.
In the new script, additionally add a new line of code at the top (before the call to ggui_menu_preallocate) which should be ggui_frames = 0; (this lets us reset the menu completely - if you haven't noticed it already the frames are containers for all other GUI elements and resetting the frame counter like this will override the old data with new data without the system noticing)
In the now-empty with statement in mev_pause_monsters_status, add a call to that new script that just passes in monsamp and monid as the arguments, and additionally my_monster = other.my_monster so that we pass in the status menu's reference to the party.
Now we can fill in those left/right events: they should first update my_monster (add or subtract 1; if below AMP_FIRST_ACTIVE or above-or-equal to AMP_FIRST_ACTIVE+PARTYSIZE_ACTIVE, add/subtract PARTYSIZE_ACTIVE to loop back to the other end of this valid range; repeat the operation if the monster in that AMP slot is empty), then recompute monsamp/monid as currently done in mev_pause_monster_status, and then finally call the menu-setup script with those values.
You can of course extend this to terminals as well, though you'd need a modified version of the left/right check step script that also knows that box the monster is in, and loops the cursor around its range.
1) Stat total is the actual total stat distribution for the monster; the values inputted for the individual stats are scaled up so that their sum will equal this value. (This means you don't need to do the math to ensure all monsters of a particular tier are balanced, and the values for each stat only need to make sense relative to each other).
2) Not currently. There's a couple of ways to fix this, I think the easiest would be to rework how special effects work: currently, it's an array that must have 4 fields (or be empty) and they're unpacked into 4 separate scalar fields in the move data structure in init_move. (Chance, type, detail and severity). But you could just store the special effects array as-is when initializing the move; this lets you have as many fields in the array as you want. Specifically, you'd wanna have zero or more sub-arrays, each of which is a 4-element array with the same data special effects have now. When applying side effects (obj_battlecontrol's Step event, after the two comments "Side effects (target)" and "Side effects (user)", you'd loop over the outer of these nested arrays, and do the side-effect checks that currently exist for each element in that array (which would be a chance-type-detail-severity tuple). So for instance Dragon Dance would have the data
And to reiterate, this is an array with two elements; they just so happen to both be arrays with 4 elements.
3) Stats are always computed from the base stats, IVs, EVs and the monster's level. The final stats aren't stored anywhere, and stat points aren't added at any time. Check out monster_get_stat and the scripts it calls for the details.
There is not; I think it would be pretty easy to implement though. Make a new "drain" side effect that applies to the target. When computing target side effects you already have access to the dmgdata variable (which among other things contains the actual damage dealt to the current target prior to applying side effects), and a_user, the battlemonster instance using the move, so it's pretty simple to apply a heal to the move's user using this information.
Great, that's simple enough. Last question for now lol, where is the IV information held for each Mon? Is it held against each Mon or is it computed somehow? And does the way EVs and IVs work in this framework an exact copy or or there some differences?
IV and EV data are stored in the AMP data for that monster (AMP = Active Monster Party, the monster individuals that are in your / the enemy's party, contrasted to the general notion of each species). So, held for each mon.
They work a bit differently in this engine, both to make them legally distinct and make monster stats grow faster so you don't need to grind levels to beat the demo. If you wanna make them more Pokémon-like, all the stat calculations are in the stat-computing scripts we talked about a couple days ago - just change them there and it will affect all the monster stats in the game.
New question. Once I've selected a move and it brings up the menu for selecting a 'target to attack' from the menu, where can I grab the monster id of the proposed target (the one highlighted in the menu) as in, the monster the player is thinking of attacking before it's confirmed?
I'm implementing a battle cursor so that it highlights the enemy visually but I can only find the initial creation of the menu listing all the targets, not what the current proposed target is going to be based on this menu. If I'm going about it the wrong way feel free to correct me.
The script mev_battle_attack_select will build this menu (listing only the targets that are valid depending on the move), and it creates an array "my_target" in the menu instance, which the menu later uses to slate the action when you confirm. Each entry in this array is the instance ID of a battle monster object, so you could just copy it (after it's been completely built) to your cursor object and then get positions etc by going through the instance IDs.
Now, your main question is how to keep this in sync with what you're selecting in the menu. It's pretty simple: script_step. Menu objects will execute this script after their regular menu-handing behavior if it's not NONE. (There's a whole bunch of these customizable behavior scripts for more complicated menus - check parent_menu's create event - but most of the time we only change script_draw)
Make a script that tells the battle cursor what the currently highlighted item in the menu is, and in mev_battle_attack_select, set the script_step variable in the menu to this script. (Heck, you could forgo the array-in-cursor entirely and just tell it to focus on an instance ID by having the menu check the array... that's probably easier?)
Also, you could use this to make the cursor automatically get destroyed when the menu is closed: in its create event and in this script, set an alarm to 2 steps. When the alarm happens, instance_destroy(). As long as the target menu exists, it will keep resetting the alarm before it can trigger, but when it's destroyed (after you confirm the target or cancel out), the alarm will run out and also destroy the target cursor.
Excellent thank you for the in depth reply, I'll have a look into what you've suggested. For my next question, is there an easy way to add the 'box' functionality into the main menu instead of accessing it from a specific place etc?
Yeah, I belive just adding the menu option to the main menu should do it.
Open up the script cc_terminal and copy the code from there, except the csc_proceed() line, and then just paste it into a new script "mev_pause_boxes", then go to obj_player's Step event to find the code where the pause menu is created when you press the pause button - add a new line here that adds that new mev_pause_boxes option.
Awesome thanks I'll try it out, appreciate the quick and professional support especially considering the sheer volume of my questions, sorry about that 😅
Me again lol. Another few questions which may or may not already been included, if they have if you could point me to those move examples that would be great.
1) Is there a way for a move to increase the damage of certain move types e.g like weather effects - Rain - increase water and electric moves?
2) Is there the ability for multi-turn moves where you charge on one turn and attack on the next i.e Solar Beam
3) Is there the ability for moves such as Protect where all damage is negated?
4) Is there the ability to force a move to go first e.g Quick attack
5) Is there the ability to force a critical hit?
6) Is there a way to force an attack to never miss i.e Swift
6) Is easy, just set the accuracy field to a really big number (all buff moves currently have 999 accuracy so they basically can't miss, though you might wanna go even further just to be sure).
1) There's no weather system currently. I'd probably implement this as a pair of global variables (weather type and remaining duration). At the end of every turn, count down the duration, and if it reaches zero, reset to "no weather" and display a message. Altering damage for certain types would be done in the script named compute_damage: simply check if e.g. it's raining and you use a water or electric move, and if so, change the multiplier variable accordingly. Effects like sandstorms/hailstorms dealing damage could be put in the same place ailment damage happens.
2) The easiest way to do this would be to implement a new Side Effect, I guess: the move itself does nothing, but has a 100% chance to add a "readied move" status which makes you skip the next turn's action assignment, and automatically enqueue the readied move (similar to how Sleep/Paralysis works), which would be special normally-unlearnable moves that unleashes the actual attack. These could be instance-variables in the battlemonster objects instead of something you store in the AMP data, similar to the buff array.
4) This is pretty straightforward, currently moves are added to a priority queue (with action_slate) using purely the monster's speed as the sorting factor. You'd add a new field to move data for "priority", which would be 0 for most moves but 1 for moves that strikes first; add a large number which will exceed the max possible monster speed stat (like 1000) times the selected move's priority value to the sorting factor when slating a move. (Also note you could give some moves negative priority to always happen last once this system is in place)
You'll probably need to increase BATTLEACTION_SPEEDBONUS_ITEM to 10,000 and BATTLEACTION_SPEEDBONUS_SWITCH to 100,000 while doing this, so high-priority moves can't happen before an item use or switch.
3) This would be a combination of the solutions to #2 and #4: give the protect move a high priority value (I think Pokémon gives protective moves 6 priority while Fake Out has 3, Extremespeed 2 and all other first-strike moves 1?) and make it apply a special 1-turn side effect (which is cleared when the turn ends) which just overrides the accuracy check ("if(random(100) <= acc-eva){" in obj_battlecontrol's Step event) - i.e., you check for the "protected" status (another instance variable for battlemonster objects) and if true, you enqueue a priority action that displays a message that the move failed due to protection rather than doing the regular hit chance check.
5) Currently there aren't even critical hits in the game, so I guess the easiest way to do this would be to add a "crit%" field to the move data and do a separate crit roll in compute_damage, returning an additional value, true/false depending on if it was a crit or not - this would affect the message and potentially sounds/visual effects... you'd also update the damage value accordingly, since crits could ignore defense... so you'd need to do this check early on. I think the easiest place to do this would be right before the "read level" segment: do a crit roll based on the new field (global.move_data[move,mvd_CRITCHANCE]) and if true, set def_buff to min(def_buff,1) and multiply atk_stat by 2. (And to make a crit guaranteed, set the crit chance to a number greater than 100)
Awesome, thanks for the in detail response there, the support you're giving is top notch.
As an aside, do you have any plan to implement any new features to the framework at any point, or is what is offered now the final product from you? More curious than anything, what you've already provided is an amazing product for games like this to get off to a great start so I'm very happy with the purchase :)
I consider it done and final - the way Game Maker projects works, it would be really hard for people to just import the new features into their existing games, anyway, so I'd actively give people a problem if I messed around with it too much. If a new GM change completely breaks it, I'll release bugfixes, though. (array_length_1d is used all over the engine but has been deprecated, for instance, so that might become an issue at some point... easy to fix once you know about it, but I don't wanna force my paying customers to go through that just because I'm lazy!)
I just want to leave a big thank you. My last comment was from 2 years ago when I bought this and I've learned so much from the code. I don't use it for any (active) project, but I learned a lot about viable database structures. Keep up the good work
I see all of these engines as learning tools (after all, once you get better at coding any pre-existing code tends to feel restrictive and annoying to deal with) but I don't usually get to see people learning so much they can leave the proverbial nest and spread their wings! Thanks for coming back and telling me about it, it warms my heart :)
Is there a script / function that I can easily call which adds a mon to the party? I'd also like to set it up so 2v2 is the standard, how do I go about that?
Not 100% sure what you mean with "add a mon to the party", is it a new mon or one from a box?
If the former, check out how mev_intro_starterselect_confirm does it: first get the first free AMP (=Active Monster Party) slot in the player party region, then generate a new monster there. The latter (swap out a pre-existing monster) is a bit trickier but there's code for this in mev_terminal_interact.
For multi battles, the important thing is setting the two variables global.player_side_monsters and global.enemy_side_monsters to the amount you want on each side (in your case both would be 2). Check out the scripts cc_battlestart_trainer and player_step_fill for trainer and random encounters respectively.
A new mon, like at the start of the game instead of the selector or a gift from an NPC for example, something like GetNewMon(monster) and it'd just go off and add it to the party if it can or a box if not, but I'll look into what you've said and go from there. And I'll look into the global vars, appreciate the response. Great framework tho, I've had to do some bits for it to work at a smaller res but that was simple enough once I'd read through the code a bit.
Another question, I'd like to give items in the inventory little icons. I was thinking that I could just add this info into the init_item and then add some code to draw the icon sprite within the inventory menu, any reason this wouldn't work?
The code when a monster is caught should do basically what you want, then (find the first free slot in the party or box structure, and then copy the monster data from the enemy section to there).
And yeah, your line of thinking for item icons is exactly what I'd recommend! Just make sure to use a "scrollable sprite" type when coding the new menu element, so it fetches data relative to the current cursor position - basic "sprite" would essentially hard-code the sprite. (All the name strings in the inventory does this too)
1) where is the flashing white cursor code located? I can't for the life of me find that.
2) Is there a 'delete file' feature already created or any other save file management implemented aside from saving and loading? (like overwrite or multiple saves from the same file etc)
Also, if there is a better means for me to ask questions/seek support then let me know :)
Itchio messages are my preferred method of communication, it's nice when the questions (and answers!) are located next to the game for assets in particular and I check my messages at least once every day (unless something really serious gets in the way).
1) The flashing white rectangle that's overlayed on top of the highlighted menu option? It's in the script ggui_draw. It's actually drawing a rectangle with vector drawing instead of drawing an asset.
2) No. I'm kinda scared of implementing stuff like this because I always worry they'll go wrong and delete someone's progress, so I prefer leaving the responsibility to the player's operating system.
Hi, this looks really good and I've played the demo for a bit and I just had a few questions for if certain functionality is included and if it isn't is there plans for it or would the engine be extensible enough to include it with some work my side.
- item to teach a move to a monster
- more than 4 moves per monster
- Abilities, special effects that each monster possesses (passive skills basically)
None of those are included by default, but since this is just a Game Maker project it wouldn't be impossible to include them.
More than 4 moves per monster would be relatively easy, just add a few more fields in the monster data structure and increase MONSTER_MAX_NUMBER_OF_MOVES accordingly. The biggest effort here would be to resize menus so more moves will fit visually.
Item to teach a monster a new move would be a bit more work (needing some custom menus) but ultimately you should be able to leverage the existing code for learning a move by levelling up here. You probably want some data structure for which monster can learn which moves, too (a big reason I didn't include this was because I figured it was too much work setting up a system like that which would still work for every user's use-cases).
Passive skills would need to be added to the monster species data, but ultimately would be similar to held items (check obj_battlecontrol's step event and look for all calls to the function "battle_item_might_affect_this" - you'd basically do the same type of checks, but for passive abilities). I.e., the skill has a trigger condition like "on damage" or "after your turn", and you check if the affect monster's passive skill has that trigger; if so, you invoke its effect script. (And then the script decides if anything happens - an ability like Flash Fire or Spiky Body only triggers its effect on certain types of moves but you would run the script every time the monster takes damage anyway and let it decide based on current attack data and such)
You can make good top-down racing engine. If all your engines are like this, we will be happy to buy the ones that are potentially useful to us. It will speed up our work on new titles. In the end, there will be little of them, but at the beginning it will be a great help.
← Return to asset pack
Comments
Log in with itch.io to leave a comment.
I tried to edit it can you share a video link to where I can just edit the source code, all of it, I search online can't find a code edit video for game maker
Here's the official GameMaker tutorial series on the code editor. (I recommend using GML Code since Visual doesn't really make things easier, just prettier to look at.)
Thank God Oh I needed this so badly, I love the Pokemon engine you made, in fact I truly think it should get more coverage so many people are desprate for one & your is right here. I will definitely watch this
@Yal 🤔 So by “League Master”, is that another way of saying “Gym Leader”?
Since there's only one boss in the demo Fielder might as well be the champion. 😉
[total-mhp-atk-def-mag-res-spd] What does total mean? I get all the others but Idk why total is there
Total is the actual number of stat points, it gets subdivided proportionally over the stats (so the mhp-atk-def-mag-res-spd values are relative - e.g. if ATK is twice as high as DEF, the final stat will also be twice as high, but the actual value that gets stored in the end is TOTAL x ATK/(sum of all 6 stats), not ATK as-is).
The reason for why it's this convoluted is so it's easy to balance monsters - if two monsters has the same stat total, they should theoretically have the same power. (You can see this in Pokémon where there's tiers like "pseudolegendary" (600 stat total), the games never draw attention to this but competitive play communities does and that's where I got the idea originally)
Hello,
With the current logic in a battle with 2v2, if you select to attack 1 monster with both of your monsters and if the first attack kills the selected monster, the attack from the next monster will 'miss' and say 'there is no target'.
Is there a simple way to update this logic so that if another monster is still alive on the enemy side it attacks that monster instead of missing? Kind of like a 'check for other alive enemies first before missing' function.
Thanks
The part which applies the move is the part (starting at line 671) which begins:
Currently it doesn't do anything special to detect targets not existing, it checks if the number of hits is 0 and the number of misses is also 0 to conclude that there was no target after the fact. (Done immediately after this loop)
So what I'd do is insert a check before this loop, which checks if the original target remains, otherwise gets a random one based on the move (using the function enemies use to pick random targets):
Perfect thanks! Works like a charm :D
Hello!
I was wondering if I could discuss a commission based on this engine? Please email me at squidbatgamesllc@gmail.com if you'd like to discuss this in more detail.
Hello!
I don't really do commissions, I'm afraid - I barely have time to bring my own ideas to life :P But if you're stuck on anything with the engine, leave a question in the comments and I'll reply to it within 24 hours.
Hi Yal, thank you so much for making this engine, making my childhood dream of making a monster collector game possible!
Recently I discovered a bug where when interacting with anything that opens a dialogue box (npcs, signs, chests, etc), it instantly reactivates once the dialogue is complete. The only way to 'get out' of an interaction is to hold down an arrow key that turns the player away immediately after the interaction ends.
I am still a beginner at coding, so I'm sorry if this seems like a silly question. I have tried messing around with deactivation alarms and checking the interaction scripts, but nothing worked.
One thing I did notice is that in the free demo version of the game there's no such issue. Could it be my GMS version? Currently, I am using the newest Version 2024.06.2.162
I tried it out and also can see it, so something has changed with GM's processing order that causes this (it was always a bug but it just didn't happen before for some reason, lol - presumably the player was always handled before the dialogue boxes but now it's handled after and thus retriggers the interaction immediately after it closes)
It's easy to fix at least, just update the "Interacting" block of obj_player's step event like so:
Then just initialize interaction_cooldown to 0 in the Create event and everything should work as expected.
Thank you for the solution!
Hey, I've been looking to create a monster collection game, and this looks like it might be a great base to start with, but I had some questions. I'm planning on using a simplified grid battle format, so battles would take place across a 4x3 grid. Is this something that would be implementable without absolutely tearing the code base apart? There are a fair number of other changes I would be making to the traditional battle system as well, including creatures being able to potentially learn more than four moves, moves that can be powered up, and more, but I think the grid is the biggest change. Is this engine flexible enough to handle those sorts of large system changes, or would I be better off starting from scratch (or with a different engine)? Either way, this seems like a great engine, it's really cool to see something like it created to help others.
Yeah, all of the grid stuff will need to be custom-made, because there's no concept of positioning and ranges in the engine currently.
What I'm thinking will need to change:
Thanks so much for all the info! Looks like it should be manageable and a good place to get started.
Hey, can I ask what you changed inside of tsprintf, for your 2.3x fix please? I had to import an older version in. This is what I currently have, thank you:
///tsprintf(format,...)
function tsprintf() {
//Trivial String Print Format
//Replaces each % percent sign with an argument, then returns the string.
//Inteded to make writing debug printouts less painful.
var c;
for(c = 1;c < argument_count;c++){
argument[0] = string_replace(argument[0],"%",string(argument[c]));
}
return argument[0];
}
The change was to not modify argument0 directly, but to use a helper variable to hold it (I don't remember what the compilation error was but presumably argument is read-only after 2.3)
The current version looks like this:
Thank you very much for the reply, I really appreciate it. Hey what generation of Pokemon would this be like, Gen 2?
There's enough differences that there's not a 1:1 mapping (partially for legal reasons). If I had to narrow it down I'd probably say "gen 3" because there's support for 2v2 battles (and horde battles) which weren't around in the GBC days.
Oh that makes sense. Thanks again for making this, it's an awesome engine!
Hey :P just wanted to confirm if it was ok to use this engine in a commercial game or not because I've seen a couple other games that are and I love how this one is set up. I'll be making everything else myself, I just would love the ability to skip the code part of it and get right into the creatives
Of course you can! It's even mentioned in the store page description. The only thing you're not allowed to do is resell it as an asset pack, anything else is fair game, commercial or otherwise.
(And just for the record you are allowed to use all of the sprites/music that comes bundled with the code in a commercial game as well, though since a lot of it are placeholder-ish I can see why you'd wanna replace them :P)
How do you make it so only the monsters that entered the battle will get exp?
How do you make in battle the move you used next time you are on the same move? So that when next turn can just press x twice in a row to do the same move again?
Both of these will need you to track some additional data, I think two global arrays with AMP_PARTYSIZE_ACTIVE slots will be enough: global.party_was_in_battle and global.party_last_move_selected.
Thanks! I got the move to remember which one it uses. I almost got the part with exp. I just it to only give exp to each individual monster based on which one they went against. How do you get for the ID of the opposing monster? So like if it's the first monster they sent out or the second.
Code I think will work to check which ones where in the active at the right time:
How would I get the player monster level and the opposing monster level in obj_battlemonster: Step? Something like this?
The enemy amp_id's start at AMP_FIRST_ENEMY and then go upwards from there, until they end at AMP_FIRST_ENEMY+PARTYSIZE_MAX_ENEMY.
So the first monster that was sent out is in global.active_monster_party[AMP_FIRST_ENEMY], the second is in global.active_monster_party[AMP_FIRST_ENEMY+c], and so on.
(IDs that don't have a monster in them has an amp_MONID of NONE, otherwise amp_MONID contains the monster ID and amp_LEVEL the level)
(PARTYSIZE_MAX_ENEMY is 12 by default but if you use the constant to figure out how long arrays you need it should work out regardless)
Thanks! I got the exp for only the monster who was in the battle working.
Bug: When using a item like a potion on the overworld, if you use it on a monster and hold right or left it will scroll the items 30 times a second(You can hear it scroll like crazy).
How would I make when I use a item on the overworld the screen would stay on the monster select thing instead of closing that and going to the items menu?
The script that applies the selected item is mev_pause_items_use_actually_use, in that you'd want to remove this code at the end which destroys the current menu and the preceding one (the "use/throw away/hold" menu):
I don't see the buggy behavior in the engine source file (ggui_menu_handle also has a check that only runs the left/right handler if you've allocated a menu that's 2 at least slots wide so it shouldn't even attempt to move the cursor in the monster menu which is fully vertical) so it might be something you've changed?
How do you add monsters to your party in the function that gives you lots of stuff to the inventory? Like adding one to the box and stuff?
You can find the code for it in the mev_intro_starterselect_confirm script:
If you wanna add it to the box instead, use amp_get_new_box_id to get the first free box slot instead of the first free party slot.
Two bug reports. In a wild encounter you can mash the x button to make the wild encounter attack before you can do anything. Also when you press the escape button in a wild encounter you can mash the x button to make it press it over and over again only when the first time you escaped.
I'll look into it, probably it can be fixed by manually altering the "active" variable so the menu ignores keypresses during the fadeout.
Hi Yal! Great job with this engine. Love the battle engine! i had a Q. How can i equip the Player with all the available Monsters in his party at the start of the game?
Easiest way would probably be to edit cis_intro_part2 and swap out the cc_intro_startermonsterselect / nickname logic; rather than making a monster choice menu and wait for your input, the startermonsterselect script would instead fill your party with the monsters you want:
First get the ID of the first empty slot in the party, then generate a monster there using its species and level, repeat as many times as you want. There's matching get-first-free-slot functions for the boxes and the temporary enemy slots, too.
To go through every monster like this, rather than hardcoding the monster_id you could loop from 0 to MONSTER_MAX.
Also note that if you really want the player to have every monster in the party, you probably want to increase PARTYSIZE_ACTIVE from 6.
Amazing!!! i love this engine so much. Also i tried to add a Monster. I tried to just input the code in init_monsters but do i need to also add the the "monster_WRAITHMANITA" somewhere else? What is the best way to add a monster. if you put this tutorial somewhere let me know thank you for your quick response and everything!
NM I FOUND IT IN CONSTANTS where to put the Macro!
If you haven't found it yet, try middle-clicking constant / function names to immediately open the script file where they're created. Super useful when navigating a large project. (So in this case, you could've clicked any of the monster_ constants to go to the file where they're defined, and that'd been the place to add new ones)
Also the ctrl-shift-F and ctrl-P hotkeys for opening global search / jump-to-asset can be useful if you know something's name but can't find it.
great short cut thank you!
Also it worked! thank you! one last thing. How i can I code so Player can run away from Trainers?
The menu event script for this is mev_battle_escape, you'd just need to remove the special case for trainer battles.
Though you might need to take further measures to prevent the player from immediately ending up in a rematch, cc_battlestart_trainer saves the player's position after they've entered the trainer's vision range. So you'd either need to keep track of a previous position somewhere so you can deposit the player out of harm's way, or maybe give the player a temporary invincibility to further battles after they get away (and while this countdown is ongoing obj_npc_trainer's User Event 2 code - which is where they look for players - will ignore them)
Ok thank you I shall attempt this!
what would be the best way to just remove the trainer from forcing me into a battle. I don't need that functionality in the game i'm building The player would initiate the battle by walking up and pressing the action button.
NM i figured it out! I think i am good to go for now!!! time to start building!~
Hi Yal! me again.. I've gotten things going and I'm almost there where this works but now I can't seem to figure out how to add a room in the indoors room. There is no code in the door object and i can't find the script that gives x and y coordinates for the indoors room. The purely script based engine is very hard to get used to. I'm used to engines with more object based code than scripting. I also added another tile set for the indoors and the engine crushed it. Is the room getting resized upon output?
I completely broke the game adding a room. I deleted it and now player spawns in the middle of the ocean :( Please help.
If you turn on the DEBUG_MODE flag you'll get some helpful printouts in the "Output" window when you load a room (check out the player's Create Event + Alarm 1 event for the code that handles where to spawn).
I think what's happening here is, you saved the game in the now-deleted room, and then when the room doesn't exist anymore the fallback code in mev_title_continue_fileselected which spawns you in the lab might not work anymore if you've changed the layout. (Or if you re-created a room with the same name but a different layout, the saved position will be used but now be in the middle of the ocean)
The door code is in obj_player's collision event with obj_door. But they're meant to be usable without adding any code, instead open the instance in the room editor and check its "variables" window:
Here you select which room to go to and a "label" (whatever text you want), the player is automatically placed at the door object with the same label as the one they entered from. So you can label doors in a town based on which character's house they lead to, and so on. (The code that loads the player based on which door they used is in obj_player's Alarm 1 event)
Not sure why the tileset is so distorted but my theory is that it might've gotten broken if it's got a different size from the existing ones? (320 x 256). Height shouldn't matter but the 320 pixel width is since the tile indices are used for collision checking (left 160px (= 10 tiles) are walkable tiles, right 160px are walls).
Also make sure the tileset settings are correct!
These should also match the grid size of the tile layer you're placing these on (A), re-selecting which tileset to use on the layer (B) will refresh how the tile layer is drawn as well.
Thank you. I figured out your label system early on. I tried to make a room in the indoor and made a door and added a new label. The player would touch the door in overworld and respawn back in the overworld. I didn't know what to do. the next thing i did was use the label of one of the already existing locations in "indoors" and altered the labels of one of the other pre existing indoors. this caused a strange confusion to occur and the tiles were crushed. I then decided perhaps it would be best to make a new room. I copied the room to ensire if there were any game code it would remain intact but upon doing so i destroyed the game. I deleted the room and then repawned in the ocean. I just checked the settings on the tiles and they are correct and also correct in the room. As you are correct the code must be sending me to the ocean of a room that doens't exist however, it has the same sound as the forrest so i think that's where its sending me. How can i fix? I may also try adding the tiles in want to the pre existing tile set provided to see if that works.
i just checked the create event and alarm 1 of the player. I have no idea how to fix.
What I was thinking was, you'd enable DEBUG_MODE (line 5 of init_constants is "#macro DEBUG_MODE false", change it to "#macro DEBUG_MODE true") and then you'll get messages telling you what happens. E.g. if a door transition takes priority, the game will print "Came through a door, jump to LABEL" (so if you're not where you think you are, you can check that the label is correct) and it'll tell you "Player loaded! (room=NAME OF ROOM)" as well so you can tell where you ended up.
You could try adding additional show_debug_message's to e.g. print the player's position after loading, that should make it easier to find in the room editor:
As I said before, going through a door will place you at the door with the same name in the other room. So a drawback of this approach is that you can't link two doors in the same room together (because when looping over the door objects it'll find whichever of the two doors with that label is first in the instance list and always pick that). If you want to warp between places in the same room I'd probably create two new objects, "obj_teleport_entrance" and "obj_teleport_exit", which has a label the same way the doors have. Then in player's collision with teleport_entrance, run this code:
Now each teleport pair would use one entrance and one exit. If you want a teleport to be in both directions you'd have entrance A and exit B on one side and entrance B and exit A on the other side; make sure to place the exit some distance away from the entrance (otherwise the player gets stuck in an infinite loop of warping between them).
Are you going to continue working on this soon?
I consider it complete (unless there's a game-breaking bug).
Hi Yal, hope you had a great Summer. I've been trying to setup a 'shop' that displays the monsters seen, and purchase/craft monsters from a list that of them you've caught.
I've taken your UI of mev_pause_moncyclopedia for the shop layout, and am attempting to use the item purchasing goodness from msh_spawn_shop_list and its menu event accomplish this.
The thing I think I'm currently stuck on is using the proper code to set up the var it which I want to be the specific ID of the monster you're hovering over .Then carrying over that info with shop_item variable to the shop menu event.
I've been using it = global.monsters_seen[d] but after starting to use the debug feature, I realized this is not giving me what I need lol.
Here's the code block I'm struggling with to accomplish that. Its the block for the mon's you've caught and thus should be purchasable. I'll link the full script code in a comment below if that helps. Thanks!!!
//Add meta data as well, based on how much info the player has about the monster.
var d, mev, it;
draw_set_font(font_n)//Needed by string_wrap
for(d = 0; d <= maxmon; d++){
vev= d
yy = lerp(0.05,0.95,d/cmax)
hh = lerp(0.05,0.95,(d+1)/cmax) - yy
// ggui_menu_region(0,vev,mev,0.05,yy,0.95,yy+hh)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if(global.monsters_caught[d]){
//Wrap flavor text so it fits the box we add below
mev = argument0
it = global.monsters_seen[d] //global.monsters_caught[d]
ggui_fill_meta(0,d,global.monster_data[d,mond_SPRITE_BATTLE],0,global.monster_data[d,mond_NAME],string_wrap(global.monster_data[d,mond_CRAFT_SLOT_SOUL_STRING],VIEW_W*0.6*0.8))
var soul = global.monster_data[d,mond_CRAFT_SLOT_SOUL]
shop_item = it
shop_cost = soul
}
function msh_botsmith_spawn_monsterpurchse_list(argument0) {
//mev_pause_moncyclopedia()
//Make sure statistics are up-to-date
msh_recompute_seencaught()
//Find max monster
var maxmon = 0, c;
for(c = MONSTER_MAX-1; c >= 0; c--){
if(global.monsters_seen[c]){
maxmon = c // maxmon is arraylength
break
}
}
show_debug_message(global.monster_data)
with(instance_create_depth(x,y,depth - 1,obj_gguimenu)){
ggui_menu_preallocate(1,maxmon +1)
//Left pane: monster list (scrollable)
ggui_frame(0,0,VIEW_W*0.4,VIEW_H,spr_messagebox)
ggui_frame_set_scrolling(false,true,1,10,false,true)
var cmax = 10, yy, hh, vev;
ggui_element_text_settings(font_mainmsg,c_white,0,1)
for(c = 0;c < cmax;c++){
vev= c
yy = lerp(0.05,0.95,c/cmax)
hh = lerp(0.05,0.95,(c+1)/cmax) - yy
ggui_element_text_scrollable(0.1,yy+hh*0.5,0,vev)
}
//Add meta data as well, based on how much info the player has about the monster.
var d, mev, it;
draw_set_font(font_n)//Needed by string_wrap
for(d = 0; d <= maxmon; d++){
vev= d
yy = lerp(0.05,0.95,d/cmax)
hh = lerp(0.05,0.95,(d+1)/cmax) - yy
// ggui_menu_region(0,vev,mev,0.05,yy,0.95,yy+hh)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if(global.monsters_caught[d]){
//Wrap flavor text so it fits the box we add below
mev = argument0
it = global.monsters_seen[d] //global.monsters_caught[d]
ggui_fill_meta(0,d,global.monster_data[d,mond_SPRITE_BATTLE],0,global.monster_data[d,mond_NAME],string_wrap(global.monster_data[d,mond_CRAFT_SLOT_SOUL_STRING],VIEW_W*0.6*0.8))
var soul = global.monster_data[d,mond_CRAFT_SLOT_SOUL]
shop_item = it
shop_cost = soul
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
else if(global.monsters_seen[d]){
mev = NONE
ggui_fill_meta(0,d,global.monster_data[d,mond_SPRITE_BATTLE],0,global.monster_data[d,mond_NAME],"???")
shop_item[d] = NONE
shop_cost[d] = 0
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
else{
mev = NONE
ggui_fill_meta(0,d,spr_unknownmonster,0,"???","???")
shop_item[d] = NONE
shop_cost[d] = 0
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ggui_menu_region(0,vev,mev,0.05,yy,0.95,yy+hh)
}
//Upper right pane: monster sprite + name
ggui_frame(VIEW_W*0.4,0,VIEW_W*0.6,VIEW_H*0.5,spr_messagebox)
ggui_element_sprite_current(0.5,0.8)
ggui_element_text_settings(font_mainmsg,c_white,1,0)
ggui_element_text_current(0.5,0.8)
//Lower right pane: flavor text
ggui_frame(VIEW_W*0.4,VIEW_H*0.5,VIEW_W*0.6,VIEW_H*0.4,spr_messagebox)
ggui_element_text_settings(font_n,c_white,0,0)
ggui_element_desc_current(0.1,0.1)
//Lowest right pane: total statistics
ggui_frame(VIEW_W*0.4,VIEW_H*0.9,VIEW_W*0.6,VIEW_H*0.1,spr_messagebox)
ggui_element_text_settings(font_nfat,c_white,1,1)
ggui_element_text(0.5,0.5,tsprintf("Species Witnessed: % Species Possessed: %",global.monsters_seen_total,global.monsters_caught_total))
ggui_element_text(0.5,0.8,tsprintf("Required Parts: %",global.monster_data[d,mond_CRAFT_SLOT_SOUL_STRING]))
}
}
global.monsters_caught and global.monsters_seen are arrays of booleans, which means each slot is true if you have caught/seen that monster and false otherwise. In this case you'd just want to carry over the counter itself, since it's the monster ID - so menuvalue_y for the currently hovered item (since it's a vertical menu).
Also, in the moncyclopedia when setting up the events (ggui_menu_region statement) you'll see we give every line a NONE event (which means nothing happens if you press the "accept" button with it highlighted), for your case you'd maybe want to make this conditional - if the monster is available for purchase (global.monsters_caught[d] is true) you'll set up a shop event (which e.g. checks if you can afford the monster and if so proceeds, carrying over the current menu's menuvalue_y as the monster ID the next-level menu refers to), if it's not available (e.g. you haven't caught it yet) you create a NONE event instead.
Thanks Yal! That explains why the monster that keeps getting purchased is Charchne, who is ID no. 1 haha. Could you expand on how I can use these boolean arrays to carry over a counter to properly match the currently hovered mon?
To pass over a value, in the mev_ script you'd do something like this:
(Similar to the scripts for doing stuff to party monsters, but instead of referring to the AMP we refer to a raw monster ID)
Hi Yal
Could you help me in the game gun 10 summer, because I have trouble finding a weapon to break the green and blue blocks. I'm currently in Western Flat, after entering the room downstairs where I'm talking to someone there, I can't go back upstairs because the shotgun isn't enough. When, after the fight, the firewall "A" opens, which I go through, and after entering the room, the computer asks for an ID that I don't have and I don't know where to get it. In this room, my shotgun is also not enough to get to the top
What should I do, what am I doing wrong
I am asking you for help
Hi Yal
Could you help me in the game gun 10 summer, because I have trouble finding a weapon to break the green and blue blocks. I'm currently in Western Flat, after entering the room downstairs where I'm talking to someone there, I can't go back upstairs because the shotgun isn't enough. When, after the fight, the firewall "A" opens, which I go through, and after entering the room, the computer asks for an ID that I don't have and I don't know where to get it. In this room, my shotgun is also not enough to get to the top
What should I do, what am I doing wrong
I am asking you for help
Hello again Yal! Do you know if this engine works OK with the most current versions of GM that have come out? I mainly ask because I know there are some retired functions (like arraysize 1D) used. I haven't updated it in forever just in case haha
also a general GM question i'm trying to figure out, I've noticed you use var c for most of you "for" loops. Does that 'c' value get carried over and iterated on in scripts that use multiple 'fors' or is the 'c' reset upon a new 'for'?
Thanks! ^.^
I tested most of my engines with 2023.8 to make sure nothing had broken, I only found a single issue (a missing sprite_exists check in the open world 3D engine). array_length_1d is deprecated but trivial to replace (array_length does the exact same thing) so I figured there's no rush, there's currently no easy way to find a list of all deprecated functions in use in a project so I'd probably just miss something, think the project is safe and then run into a massive tech support nightmare when a new GM update drops anyway.
As for the for loop variable name, I'm just using "c" because there's a programming language called "C++" so writing "c++" as the increment operation is funny. That's the whole reason :P
Anything declared with "var" only exists in the piece of code it belongs to, so c's in different scripts will not affect each other. And with multiple loops in the same script, you reset the variable to 0 at the start of the for loop, so it doesn't matter it's reused. (The exception being when you have a loop inside a loop - then you need separate variables so the inner loop won't mess with the outer loop).
Hey Yal, I picked the engine up a couple days ago and I love it, I have 2 questions and I was wondering if I could get some help or advice.
1. I've redone the move points system to be a pool of points that each move pulls from. I was wondering is there anyway to check to see which side used the attack so the enemy doesn't use the pool of points as well?
2. How does the enemy attack selection work? It looks like it puts all the attacks into a list and then shuffles them around and picks the first one. I was wanting to change it so it's choices are weighted based on mp cost/turn count, so higher mp moves are selected as the fight goes on.
Thanks Yal
1: There's a 'user' field for enqueued actions, for instance the a_user variable in the ctst_TURNQUEUE state in obj_battlecontrol's step event. (this state is probably when you want to consume the points, since it's when the action actually happens). This "user" field is a battlemonster object ID so simply check its side variable: if it's equal to side_ENEMY it's an enemy and shouldn't consume points.
2: Correct, enemies just pick a random move this way (add all valid actions to a list, shuffle it, pick the first option). If you want something priority-based you could rework what happens in the bcontrolstate_ACTIONSELECT_ENEMY segment to use a priority queue instead (ds_priority functions), where you insert things and they're automatically sorted so you can pick the highest priority value - it's pretty much analoguous to how ds_lists work and the only hard part is figuring out how to compute the priorities - you might want to add some new fields to the move database for storing base priority and priority increase per turn to make this easier to control on a case-by-case basis, and maybe rework target selection a bit to account for type matchups (i.e., add every possible move-target combination to the list but multiply the regular priority with the type multiplier for that target)
(Also you might want to still add a little bit of randomness to the priorities, so the AI doesn't become completely predictable and the player can exploit it)
I've also found a bug whereby the game will crash if you try to catch a monster that has already been captured in the same turn, because there is no longer a target. So two player party members, on both party members turn you use a monster ball item, if on the first party members turn the monster is caught, the second party members turn tries to use the monster ball but they can't be captured again, causing a crash. I fixed this by checking in the battle_control before using an item, if the item is a catching item type if the target is still 'alive', and if not, tell the player that it failed etc. If you can replicate might be worth fixing for everyone etc
Oh yeah, might need to have a look into that. I know even the released version of Pokémon Colosseum had a bug which let you duplicate items if you used an item as the first party member and then reordered items on the second party member's turn (so you could get infinite Master Balls that way) so it seems this kind of stuff is tricky to get right.
Hello, another question, how would I go about making it so that the 'box' management makes it so you have to keep two monsters in your party instead of just one? I'm focusing exclusively on double battles and don't want the player to have only one monster in their party at any point
You'll need to change the mev_terminal_grab script a bit. This line
should be changed to
and now emptying either of the first two slots in the party (by going below 2 party members) should cancel the operation.
(Also, you probably want to update the message that is printed when this happens to read "2 monsters" instead of "1 monster", a little further down in the script)
I just realized, doing it this way still will cause some issues since grabbing a party monster will move any monsters after it in the party lineup forwards one slot, and the undo overwrites the original slot (so grabbing the first monster when you have only 2 in the party will move the 2nd monster to the 1st slot, then it's overwritten by the undo).
It's probably safer to move the party-amount check to just after line 18 (inside the second "if(menuvalue_x < columns_party){" block), and have the check be something more like this:
So basically, disallow even picking up the monster if you don't have at least 2 after the operation.
Thank you. If I'm understanding correctly, this will also mean that the player can no longer 'swap' a monster in the party for one in the box if they have only 2 in the party meaning they will first have to get the monster they want from the box and add it as the 3rd party member, then put the one from the party into the box they don't want, correct?
I actually went about this a different way in the end. I modified the obj_terminalmenu step event so that you can't cancel out of the menu if the party has less than two members and put a warning if so. I remove the 1 party member warning from the grab so that you can freely move monsters however the player wants. I can't think of a reason why this wouldn't work, can you?
This sounds like it should work as well, it doesn't matter if you enter an invalid state as long as you're forced to revert to a valid state before you can close the menu. Just be careful about implementing autosaving, new ways to cancel the menus (etc) so the player can't end up stuck with too few monsters that way.
Yes for sure, I realised as well that there is not a release function in the box menu, at least, not one I can see??? So if that's the case and I implement one, I'll just need make sure that the party + box monsters is greater than 2 before you release a monster 😌. But anyway, big thanks for your help and the engine, has been a little tricky to get my head around some things but its progressing nicely
Hello again Yal! Hope you're having a good summer,
I've been working on a shield system by copying how HP works and changing all the variables to ShieldHP essentially. I'm having trouble figuring out how to get the damage to "jump" over to reducing normal HP after the shield bar has been destroyed. ( I have separate dmgapply scripts for shield dmg and normal dmg below) It seems like the damage is stuck on trying to take from the sheildHP pool even after it has reached zero. Probably some simple thing i've overlooked :) Here's what I'm working with:
//Damage
if(pow > 0){
var dmgdata = compute_damage(a_user,trg,a_comm);
if(sheildcheck >0){
n = instance_create_depth(trg.x,trg.y,depth,obj_dmgapplyshld)
n.my_dmg = ceil(dmgdata[0]*ail_power_multiplier)
n.my_mul = dmgdata[1]
n.my_eff = dmgdata[2]
n.my_mon = trg
n.my_user = a_user
n.my_move = a_comm
n.this_is_an_attack = true
}
//********************
if(sheildcheck < 1){
n = instance_create_depth(trg.x,trg.y,depth,obj_damageapply)
n.my_dmg = ceil(dmgdata[0]*ail_power_multiplier)
n.my_mul = dmgdata[1]
n.my_eff = dmgdata[2]
n.my_mon = trg
n.my_user = a_user
n.my_move = a_comm
n.this_is_an_attack = true
//User might hold an item that improves attack effectiveness
if(battle_item_might_affect_this(a_user.amp_id,itemvalidflag_AUTO_ATTACKING)){
var it = amp_read_var(a_user.amp_id,amp_HELDITEM)
n.my_mul *= script_execute(global.item_data[it,id_USESCRIPT],[a_user.amp_id,trg.amp_id,a_comm],global.item_data[it,id_USEARG])
}
}
}
Thanks!!
The code you posted seems to make sense. The interesting thing here is how you compute sheildcheck, and you didn't include that bit of code...
Also, just to get the obvious out of the way: obj_damageapply still does HP damage and not shield damage, right? (So you didn't accidentally change that one as well when you changed all the variables)
(Also note that your two sheildcheck checks are >0 and <1 so both of them will occur for values between zero and one! Is this intentional?)
trg here is the target monster object and we've already checked that it's alive, so you really only need to check if amp_read_var(trg.amp_id,amp_SHIELDHP) is greater than zero: if so, deal shield damage, else deal regular damage.
Hello again,
I'm trying to make it so that when the cursor is over a move in battle, where the box that shows the type of the move and it's MP, I can add in a sprite to indicate the type. I'm looking in the attached image but I'm not sure what element to use to get it to draw the correct icon (I already have the move data setup with the type icons, I just need to know how to draw it)
gguielt_CURRSPRITE element type (added with ggui_element_sprite_current script) is the icon sprite of the currently hovered item in the menu, this is what you want to use here.
Since the data is filled into the menu using ggui_menu_add_option_text it fills the sprite data with NONE, but you could copy ggui_menu_add_option_text and create a new script ggui_menu_add_option_text_and_sprite that also fills the sprite/image data (the two arguments to ggui_fill_meta that are set to NONE within ggui_menu_add_option_text) using two new arguments at the end, and then use that instead in mev_battle_attack. (There's already the _icontext and _gridimage helper scripts that fills the sprite data, but they also set up the elements so that the image is used directly there in the body of the menu, which isn't what you want for this - we want this new script that fills the icon data but doesn't use it yet)
Ah I see thanks, I'll give that a try
Hello again Yal, happy July :) Here's some things I've been having trouble understanding lately:
How to use properly reference a AMP (friend and foe) within obj_battlecontrol. I'm working on shield system, (where mons have to break through a shield before being able to attack the enemies HP)
I'm trying to do a check on a new stat (shieldHP) and if the AMP's shieldHP stat is >1, a visual shield object will appear in front of a mon after it's summoned.
My problem is simply trying to understand how you have been using the amp.id variable in obj_battlecontrol do to similar stat checks. I've tried a few things and placing the code in a few spots. I don't think I quite get what variable to place in front of it (with the period separating amp_id.
Here's the current code I'm trying in bcontrolstate_ACTIONSELECT_STEP 1 (around line 320)
var getshld = monster_get_maxshld_hp(action_monster.amp_id);
if(getshld > 1) {
instance_create_depth(cx,cy,d_player + -100,obj_battlemonster_sheild)}
the error messages basically keep saying the xyz.amp_id variable is undefined. Any help understanding this is greatly appreciated as always :)
I think you're going about this the wrong way - if you want a visual effect on a per-monster basis it might be easier to have obj_battlemonster handle it? It's already aware of which AMP data block it owns so it's a bit easier than going through the control (who manages a ton of monsters at once) Plus, with your current approach you'll create a new shield effect object every turn - if you don't remove them afterwards you'll end up with dozens or hundreds of them in every battle...
In the draw event, add some new code like this:
There's ways to refine it, like adding effects whenever the shield status changes (broken / regenerated) by keeping track of the status the previous step and doing stuff when it's different from the current step. But the important part for now is making it draw properly.
Hi Yal, any idea why I'm getting errors in global.active_monster_party? I've been so tied up in working on totems and menus, when I went back to look at battles, this happened :( Something to do with the Macro NONE i'm sure, but I cant figure out what.
ERROR in
action number 1
of Step Event0
for object obj_battlecontrol:
Push :: Execution Error - Variable Index [-12341] out of range [419] - -5.active_monster_party(100075,-12341)
at gml_Object_obj_battlecontrol_Step_0 (line 110) - var montype = global.active_monster_party[n.amp_id,amp_MONID];
#########################################################gml_Object_obj_battlecontrol_Step_0 (line 110)
If I comment out the block starting from line 110, then I just get the same message in the obj_monster HUD
Push :: Execution Error - Variable Index [-12341] out of range [419] - -5.active_monster_party(100075,-12341)
at gml_Script_amp_read_var (line 3) - return global.active_monster_party[argument0,argument1];
#########################################################
gml_Script_amp_read_var (line 3)
gml_Script_monster_get_name (line 4) - var s = amp_read_var(amp_id,amp_NICKNAME);
gml_Object_obj_battlemonsterhud_Draw_0 (line 12) - draw_text(xx,yy,monster_get_name(amp))
Thanks so much!
Interestingly, the problem does not occur if the battle is a hoard battle... Hmm.. There must be something I need to fix with 1 v 1 battles I think
The thing that's happening here is that you try to read the data of a monster that has a NONE AMP reference. It's specifically being done in the block that creates the enemy battlemonster objects.
From what I can see, the issue happens when you start a horde battle without generating enough monsters to fill all enemy horde slots. (This is why it works in 1v1 battles - you always generate 1 enemy monster so creating 1 battlemonster will avoid reading outside the available data)
Should be simple to solve, at least. Wrap this block
in a copy of this condition
and it shouldn't try to read the unset data anymore. (Note how the inner block also changes the monster state from "DEAD" to "NORMAL" when doing this, which will also lead to issues down the line even if you ignore the data reading bit - this is what causes the second crash)
Actually, the reverse is working (horde battles working, 1v1 battles are not) but I tried adding that condition but then got a similar error to what I have been getting:
##############################################
ERROR in
action number 1
of Draw Event
for object obj_battlemonsterhud:
Push :: Execution Error - Variable Index [-12341] out of range [419] - -5.active_monster_party(100075,-12341)
at gml_Script_amp_read_var (line 3) - return global.active_monster_party[argument0,argument1];
##############################################
gml_Script_amp_read_var (line 3)
gml_Script_monster_get_name (line 4) - var s = amp_read_var(amp_id,amp_NICKNAME);
gml_Object_obj_battlemonsterhud_Draw_0 (line 12) - draw_text(xx,yy,monster_get_name(amp))
sorry for the trouble! I really appreciate your help as I am still trying to make sense of the ins and outs of the battle system
Either way it's still caused by there being NONE monster IDs in the list of enemies to fight. Weird.
I had a little dig in the codebase and I think I have another idea for what could cause this. The code that checks for valid monsters to send out only checks if HP > 0. If the matching AMP ID's amp_MONID is NONE then we have an invalid monster ID and reading data from that will cause issues.
Do you set up / clear data for totems properly (similar to how the engine handles enemy data in random encounters)? It feels like there might be something going on with partially initialized data that causes this.
One idea might be to extend battle_get_valid_reinforcements a bit so that line 6 checks this condition instead:
if(global.active_monster_party[mon,amp_HP] > 0 && global.active_monster_party[mon,amp_MONID] != NONE){
It's not fixing the core issue that causes the invalid data but it at least stops trying to spawn a nonexistant monster. Maybe it'll be enough, since most other AMP manipulation scripts checks if the monster ID is NONE to check if a slot is valid.
Hello again Yal! This time i'm back with more general questions that I can't seem to figure out yet.
I'm using the framework of msh_spawn_inventory and how it conveniently lets players tab through multiple pages. The way i'm trying to use it, I need certain items to be different font colors but I cant seem to figure out how as this uses ggui_fill_arbtext. any advice appreciated :)
Also, could you give tips on how we might be able to display animated sprites within the menus/cutscenes. They all seem static from what I can tell.
Thanks so much as usual!
Let's start with the easy bit, animated sprites. Just create a GGUI sprite element with the subimage set to -1, and it should work (draw_sprite uses the current animation subimage of the object if you draw a sprite with subimage -1, and sprite elements are drawn with draw_sprite passing through all parameters as-is). Potentially you might need to give the menu object an animated sprite as well (by assigning sprite_index) but I think it'll work even without it.
Now for "certain items with a different color": It's a bit unclear what you mean by "items" here? Items in the inventory or GGUI elements?
The idea with (Scrollable) Arbitrary Text elements is that you have a text field that reads data from the currently highlighted menu item rather than being a preset text (they're "arbitrary" as opposed to the Description field which originally was the only per-element-changeable text, and hardcoded text strings - I added the arbitrary text fields because I realized sometimes you need more than one per-element text field).
So basically, the element itself has no text, just a reference to the Arbitrary Text Array for that menu. But the Arbitrary Text Element owns the formatting (font, color, alignment) so by default, it can't change depending on what's highlighted - you'll need to make a new type of element which accesses an array of both text and formatting information (studying how gguielt_ARBTEXT works internally and copying the behavior for a new gguielt_ARBTEXTWITHFORMATTING might help here - you'd use an array similar to menu_arbtxt but rather than just storing text, you'd store a tuple [text, font, color, halign, valign]).
But if you just want different fields to be different colors (e.g. HP is red, money is yellow) it's very simple, use ggui_element_text_settings to change formatting parameters (all further text elements are created using these settings until you change them again).
Thanks Yal :D The frames do animate at -1, but it seems to animate very fast. (maybe 60 fps fast? lol) any thing we can do to set the speed?
Hmm, I guess this is a side effect of the menu not having a sprite, so it doesn't use the sprite's animation speed... welp, it's easy to fix at least.
Set image_speed to a value lesser than 1 from the menu object's perspective (i.e. inside the block that starts with the "with(instance_create_depth(...obj_gguimenu)" call).
I find values between 0.1 and 0.2 work the best for legacy sprites that default to 60 fps, so start off with 0.15 and adjust as needed until you're happy with the result?
Hi Yal! Hope you've been well. Any ideas on the best way to implement a "totem" system?
Basically, certain moves create totems - essentially a temporary, targetable object that gives buffs, etc until they are destroyed.
I thought the best way would be to add them to the Dex .Then have them sent out, then destroy the instance when they reach 0 HP or at the end of battle.
I'm not sure how to approach adding a temporary mons to the battlefield (also considering the opposing team could use this move!) I'm thinking the move would have to temporarily increase the max team size to work.
Any thoughts are appreciated :)
Good job sticking with the project for 6 months! I'm used to people giving up much sooner but seems you're one of those rare determined people that actually finishes their games ^__^
Your analysis is pretty much correct!
Adding temporary mons would be pretty easy, as you can see in obj_battlecontrol's step event ctst_ACTIONSELECT block, it loops over all existing monster objects when building the list of who gets to act, so just creating instances should work. You should add a new "is_totem" variable to the battlemonster object so that totems are treated differently, though (it's always false, but when creating a new instance for a totem, you'll set it to true after creating it).
You might wanna do some changes in obj_battlemonster's bmonsterstate_DIE to change the KO messages for totems (and delete the object if dead - an empty slot currently still is a monster object!) so they're not around when the ctst_REINFORCEMENTS step happens, or that team will be able to send in additional monsters to take the place of any destroyed totems, turning it into a permanent team size upgrade for that battle.
You will also need to change this block:
Totems should always go to bcontrolstate_ACTIONSELECT_ENEMY (automatically select), regardless of if they're player or enemy. (Unless you want players to be able to control what their totems do, of course - I'm just interpreting your description as totems being kinda passive/automatic)
Their buffs could be implemented as new moves with the appropriate target range set up (e.g. movetarg_ALLALLY to buff all allies), then you have the new totem species learn the buff-moves it should provide at level NONE (i.e., always knows them).
Since all monster stats are read from the AMP and the battlemonster objects just have a pointer to that (rather than having their own stat variables), I'd say you could put totem data in the "enemy" section of the AMP (since that's temporary data which gets cleared after a battle) - just run amp_get_new_enemy_id / amp_generate_monster using the totem species for this. You just need to manually free up slots again (with amp_clear_monster) when a totem is destroyed so you don't run out of slots entirely if a long battle has many totems created and destroyed.
Thanks so much Yal! I am still familiarizing myself with the engine, but as i've taken your above advice into action, I realize I may be in over my head with a this rather complicated totem mechanic and may need to revisit it later. BUT if you have time, these are a few things I'm running into:
- Not sure where in the init moves to put amp_generate_monster(1,monster_totem_1,1)
I've tried placing it in a few of the argument fields there with no success. Maybe I need to create a "status" with 100% acc and code it to create totems that way?
- I have tested forcing a mon to appear in battle by just doing a press key event in obj_battle_control. I can't get it to pop in a duo battle (with one mon on my team) but it will pop into my party (and can be swapped into lol) I wonder if creating a designated slot for totems might be the way to go, unless I'm missing something which is likely lol.
Thanks a bunch as usual!!
The easiest way to do really weird things that doesn't fit into the standard move data would be to hijack the "aniobject" argument (mvd_ANIMATIONOBJECT slot of move data). It's intended to be used just for visual effect controllers, but nothing's stopping you from creating an object which in turn sets up a totem, or does other weird stuff (a boss could have a move that plays a cutscene, for instance). The only thing to keep in mind is that it needs to be a child of parent_battleeffect if you want the battle to pause until the object is destroyed (i.e. it's gonna be around for more than one frame). Do the behavior you want to achieve in the step event, since we set some variables after creating it (and thus they're not accessible in the create event).
Also the code you're running is probably not doing what you think you're doing...
The function is amp_generate_monster(slot,species,level). First argument is slot, if you just put an "1" there it's always going to overwrite whatever's in the second party slot (array numbering starts at 0 in Game Maker). You want to ask the engine for an unoccupied slot, so you probably want
xx, yy and dd are the X, Y and depth values to create the totem on, you'd have to compute them somehow. For this you could use the fact that effect object you created has a variable "user" set by the battle controller, which is the ID of whoever ran this move, so if you e.g. want the totem to spawn in front of the user, you'd do something like:
(potentially you might also wanna use the "target" variable which is the move's target, e.g. if you could create a totem that affects someone else in a multibattle)
Hi Yal thanks for everything :) Its a great engine. I was wondering what I could do to implement mouse controls? When I hover my mouse over the game window, the cursor disappears. Thanks again
Let's start with the easy part, how to enable the mouse cursor. Open the Game Options, then Windows, then the Graphics tab and check this box:
(if you want to draw your own cursor you could draw a sprite at the built-in variables mouse_x, mouse_y - each of the control objects could do this)
Having actual mouse control is considerably more complicated but I think something like this would be the easiest way:
Now this will remove the "cancel button" option you had before but you could solve that by adding a "X" button in the top right corner. (You still need to preallocate enough menu options for all buttons but since the player needs to physically hover them you could put any "padding" buttons offscreen if you need to allocate more buttons than you'd physically use)
(actually maybe it would be easier to handle the X button out of GGUI entirely - just draw a "close" button at the top-right of every menu frame and check if you're in the top right 16x16 pixels of the menu in the Mouse Left Released event, if so destroy the instance and don't even check for frames)
There's more stuff you could do here, like playing the "menu move" sound when you detect that you changed either menuvalue_x or menuvalue_y, but this should be a good starting point.
Thank you so much Yal! I can't believe the thoroughness and speed of your replies :)
This is a great start for me, I'm very new, and still making sense of your scripts/variables in my head. I'll be sure to implement this mouse implementation once I get a better grasp of the overall engine.
Hello again. I've been trying to get mouse controls to work. I managed to get the mouse buttons working no problem, but I'm having a fair bit of trouble getting the menu to figure out which menuvalue_x and y the mouse should be on. I tried the code above, and it only seemed to partially work. On the main menu it only every selects New Game. On the player name select it goes to 'M' immediately, then if you click it selects all the letters from A-L.
I also tried a different system where I defined some variables and used them to figure out the menuvalue_x and menuvalue_y. The variables I used:
to try and get this code to work:
I'm not quite sure what I missed, but it would be great to get mouse controls working, and any help would be greatly appreciated.
First of all these give you the wrong sizes, just using the frame sizes should be enough (don't add the left/top position to the width/height)
I also don't see why you divide the pixel size with the number of elements along the axis, they represent the same ratio if evenly spaced and that means translating between them should "just work". Though you'll need to make sure the output stays in range:
(And also note that "If evenly spaced" might cause issues with the more esoteric layouts since nothing forces a menu to actually be evenly spaced, you can place frames anywhere and fill them with whatever you want)
Did you check the code in ggui_draw that draws the rectangle over the currently selected item? It converts from grid coordinates to screen pixel coordinates so it might help a bit here. You could even skip having to do the reverse computation (grid coordinates from screen coordinates) if you loop over all menu options, compute their corresponding frame coordinates (using this code), and then use point_in_rectangle to check if the mouse cursor is over that frame. Slow, but 100% accurate.
(Note that drawmenux/drawmenuy, the scroll offset, are computed at the beginning of the function)
I got it to work! First, thank you so much for you help Yal! Your help has cleared up a bunch and made it so I could figure this out. It took a bit to try and understand how the menus work, and I still don't get it all, but for those interested in getting the mouse to work, here's what I did:
First, in the init_input script I added:
Then in the function get_keys script I added and changed a couple lines:
Then I found pretty much everywhere that k_a was and made it (k_a||k_ma). And the same thing with k_b, I changed them to (k_b||k_mb).
This was to allow for the mouse button to work as the 'A' or the 'B' button, while still keeping the regular 'A' and 'B' button working. So you can use either the mouse or the keyboard.
The part that I had trouble with was figuring out which menu box the mouse was in. What I ended up doing was looping through the options to see if the mouse was hovering over an option. Than looping through the options and checking if the mouse was in each rectangle until it found the right one. It's on the more expensive end, it requires a few loops, but it does seem to work, and I haven't noticed any issues.
So, here's what I put, right near the end of ggui_menu_handler (just above the closing }):
If anyone has a way to refine this and make it less slow and costly, I'd love to hear it. If what I have put above is unclear, let me know. But, this is what I did, and the mouse seems to be working well.
I was wondering is there a PDF for it or just reference the features mentioned to edit it & thank you
There's no manual, no. Great idea though, I'll see what I can do!
Thanks
i would also love a manual. with some simple tutorials on adding mons, setting up tilesets, stuff like that. great template though! i'm loving this
Hello, question! I want to change the height and width to 1200 x 800 ( or full screen ) so i can play on a bigger screen. Now is my question where i have to start to make it work? I know i can change the height and width of the room, but which another things i have to change?
Thankyou.
The majority of GUI elements are based on the VIEW_W and VIEW_H macros, update those to match the desired screen size and a lot of things should "just work". (Battle elements and some misc stuff use the room size for positioning instead so make sure to update all "1x1 screen"-sized rooms like battle, evolution etc to match the new size). Also on overworld rooms, the viewport size + camera follow border size needs to be updated, since views are set up in the Room Editor instead of by code and they're all hardcoded to 640x360 / 320x180 right now.
You might need to make some sprites, fonts and other graphics elements bigger as well, since they might look weirdly tiny on a much larger screen (and you also change from a 16:9 to a 4:3 aspect ratio which might squish some things horizontally) but that's an optional UX thing and not something that's technically necessary for this to work.
Hello, once again. Quick question:
Is there way to make it so that an NPC can only be interacted with from a specific direction?
Right now interactions are handled in obj_player's step event, lines 81-98 (the block starts with a comment that reads "//Interacting"). It's a basic collision_circle check so there's two ways you could solve it:
Hello - me yet again with another question coupe of questions.
1) What would be your advice for setting up multi hit attacks e.g fury swipes, double slap
2) What would be the best way to make a move that gets more powerful if used in succession e.g rollout, fury cutter
2) Is the easiest... since you've got access to the user's object instance you could add two local variables to obj_battlemonster, the last move used and how many times it has been used in a row; the times-in-a-row counter would be incremented by 1 if you use the same move as last time, otherwise reset. (You might also wanna reset it on switching, if you use an item, fail to act due to ailments, etc). You'd add a new "stronger if used in succession" flag to the move data and compute_damage would take the times-in-a-row counter into account if the move has the flag set.
1) Probably also is easier with new movedata fields; min/max number of hits. (Two separate fields if you want to allow moves to have a random number of hits - then you'd pick a random number between these with irandom_range. Moves that always hit the same number of times would have the min and max values be the same)
Where to insert this is a bit trickier, you could slate multiple actions but that duplicates messages and would run ailment checks multiple times. Another idea could be to make a separate "multi-hit deployer" object which is spawned after the regular damage/side effects block when the current attack has multiple hits; it would be a child of parent_battleturn so the turn doesn't automatically proceed, and would have a copy of the user/target data, the index of the move being used, and a counter of how many more hits it should cause. Whenever there's only one parent_battleturn left (remember, we're one too so the counter won't hit zero), it would run a copy of the damage/side effects code (ideally you might wanna break that out into a script to make it easier to keep adding new things in the future rather than actually duplicating it in two places ?) using the user/targets/move data it got a copy of, decrementing its hit counter each time, and if all targets are dead or the hit counter reaches 0, it destroys itself.
Hey, thank you so much for this engine:) I am learning a lot from it, all your stuff here on itch.io looks totally awesome! I also read through all the questions here to learn even more about the engine!
Also, as an advice for other mac users considering buying this: This code worked fine on my mac, but to get it running you have to open and save it at least once in windows. Thats at least how it was for me!
For now, I have two questions:
1: I would like to implement a minigame when the enemy attacks, kind of like Undertale. Depending on how many times the opponent hits during the minigame it would cause the player damage. Is there a way to implement something like this?
2: Next to four attack moves I would like to also give the player four dialog options, giving the player the opportunity to either fight or talk their way to success during battle. Any Idea how I could implement this in an easy way?
Thank you so much in advance!
Intriguing; I know GM is a bit weird with cross-compiling setups but I've never heard of anything like that. I guess some project paths won't get updated automatically unless you save on Windows and it was still using the ones from my computer...?
Anyway, onto your questions:
1) The attack is applied inside the loop in obj_battlecontrol's step event at line 672 (after the "//Apply to all target(s) individually" comment). The variable trg is set to each target individually; for your use-case I guess you would want to check whether that is a player or enemy character and do different things (it's the ID of a battlemonster object so just check its side variable) - for enemies you can keep the current system, but for players you'd branch out and create a minigame object instead, and it is responsible for applying damage, side effects etc depending on the minigame result. If it's a child of parent_battleturn the RPG turn flow should automatically wait until it destroys itself with no further action from your side.
(Basically add an if statement that checks if trg.side == side_PLAYER, if so create the minigame; then put the entire existing code into the else block)
If you want different minigames for different attacks, the easiest would probably be to extend the move data (see init_moves / init_move) and add a new field for "minigame spawn script" which is run when the move is performed, creating the appropriate minigame object(s) and setting parameters for damage etc.
2) I'm thinking the easiest way would be to give each enemy monster species an array of dialogue-options / script pairs, and then you start a cutscene where you immediately deploy a cc_question that uses it - basically what cis_shrine does, but you'd use a monster-species-specific variable for the menu alternatives rather than a hardcoded array; each of the scripts that are run when you pick an alternative would either display dialogue, enqueue extra attacks, or alter a "progress" variable in the affected enemy (or whatever else you want the dialogue system to be able to do)
For this system to work, there's a couple things you need to do:
Yeah my guess was that mac needed the code saved in the most recent IDE to get it working. Anyways it works fine now!
Thanks a lot for the detailed advice, I will give it a shot!:)
Hi me yet again,
When viewing a monsters status from the menu, is there any way to make it so that if you press say the up / down or left / right keys, it'll change to the next monster instead of having to back out each time and select a new monster?
Thanks
It's gonna be a bit complicated but I think this should work:
You can of course extend this to terminals as well, though you'd need a modified version of the left/right check step script that also knows that box the monster is in, and loops the cursor around its range.
Great thanks for this, I'll give it whirl and see how I get on!
Hello,
Maybe this is already asked but i can't find it.
Can you use your own art for the engine?
And i am learning to program / make something for myself and i think this can help me to understand more how to make a monster catching game
Thankyou
Yes, the engine is just a Game Maker project so you can replace anything you want!
Hi there again. Three new questions
1) What does the total stat do inside the monster setup stat array?
2) Can a move have multiple special effects? Example Dragon Dance, raise Atk and Speed?
3) Where is the stat increases upon level up dictated?
1) Stat total is the actual total stat distribution for the monster; the values inputted for the individual stats are scaled up so that their sum will equal this value. (This means you don't need to do the math to ensure all monsters of a particular tier are balanced, and the values for each stat only need to make sense relative to each other).
2) Not currently. There's a couple of ways to fix this, I think the easiest would be to rework how special effects work: currently, it's an array that must have 4 fields (or be empty) and they're unpacked into 4 separate scalar fields in the move data structure in init_move. (Chance, type, detail and severity). But you could just store the special effects array as-is when initializing the move; this lets you have as many fields in the array as you want. Specifically, you'd wanna have zero or more sub-arrays, each of which is a 4-element array with the same data special effects have now. When applying side effects (obj_battlecontrol's Step event, after the two comments "Side effects (target)" and "Side effects (user)", you'd loop over the outer of these nested arrays, and do the side-effect checks that currently exist for each element in that array (which would be a chance-type-detail-severity tuple). So for instance Dragon Dance would have the data
And to reiterate, this is an array with two elements; they just so happen to both be arrays with 4 elements.
3) Stats are always computed from the base stats, IVs, EVs and the monster's level. The final stats aren't stored anywhere, and stat points aren't added at any time. Check out monster_get_stat and the scripts it calls for the details.
Ah I see. Thank you, that makes sense. I'll check out those scripts and see what I can do there, thanks again. Another (hopefully easy one)
1) Is there a move / effect already set up to recover some HP relative to the attack i.e absorb or giga drain for example.
There is not; I think it would be pretty easy to implement though. Make a new "drain" side effect that applies to the target. When computing target side effects you already have access to the dmgdata variable (which among other things contains the actual damage dealt to the current target prior to applying side effects), and a_user, the battlemonster instance using the move, so it's pretty simple to apply a heal to the move's user using this information.
Great, that's simple enough. Last question for now lol, where is the IV information held for each Mon? Is it held against each Mon or is it computed somehow? And does the way EVs and IVs work in this framework an exact copy or or there some differences?
IV and EV data are stored in the AMP data for that monster (AMP = Active Monster Party, the monster individuals that are in your / the enemy's party, contrasted to the general notion of each species). So, held for each mon.
They work a bit differently in this engine, both to make them legally distinct and make monster stats grow faster so you don't need to grind levels to beat the demo. If you wanna make them more Pokémon-like, all the stat calculations are in the stat-computing scripts we talked about a couple days ago - just change them there and it will affect all the monster stats in the game.
New question. Once I've selected a move and it brings up the menu for selecting a 'target to attack' from the menu, where can I grab the monster id of the proposed target (the one highlighted in the menu) as in, the monster the player is thinking of attacking before it's confirmed?
I'm implementing a battle cursor so that it highlights the enemy visually but I can only find the initial creation of the menu listing all the targets, not what the current proposed target is going to be based on this menu. If I'm going about it the wrong way feel free to correct me.
The script mev_battle_attack_select will build this menu (listing only the targets that are valid depending on the move), and it creates an array "my_target" in the menu instance, which the menu later uses to slate the action when you confirm. Each entry in this array is the instance ID of a battle monster object, so you could just copy it (after it's been completely built) to your cursor object and then get positions etc by going through the instance IDs.
Now, your main question is how to keep this in sync with what you're selecting in the menu. It's pretty simple: script_step. Menu objects will execute this script after their regular menu-handing behavior if it's not NONE. (There's a whole bunch of these customizable behavior scripts for more complicated menus - check parent_menu's create event - but most of the time we only change script_draw)
Make a script that tells the battle cursor what the currently highlighted item in the menu is, and in mev_battle_attack_select, set the script_step variable in the menu to this script. (Heck, you could forgo the array-in-cursor entirely and just tell it to focus on an instance ID by having the menu check the array... that's probably easier?)
Also, you could use this to make the cursor automatically get destroyed when the menu is closed: in its create event and in this script, set an alarm to 2 steps. When the alarm happens, instance_destroy(). As long as the target menu exists, it will keep resetting the alarm before it can trigger, but when it's destroyed (after you confirm the target or cancel out), the alarm will run out and also destroy the target cursor.
Excellent thank you for the in depth reply, I'll have a look into what you've suggested. For my next question, is there an easy way to add the 'box' functionality into the main menu instead of accessing it from a specific place etc?
Yeah, I belive just adding the menu option to the main menu should do it.
Open up the script cc_terminal and copy the code from there, except the csc_proceed() line, and then just paste it into a new script "mev_pause_boxes", then go to obj_player's Step event to find the code where the pause menu is created when you press the pause button - add a new line here that adds that new mev_pause_boxes option.
Awesome thanks I'll try it out, appreciate the quick and professional support especially considering the sheer volume of my questions, sorry about that 😅
No problem at all!
Me again lol. Another few questions which may or may not already been included, if they have if you could point me to those move examples that would be great.
1) Is there a way for a move to increase the damage of certain move types e.g like weather effects - Rain - increase water and electric moves?
2) Is there the ability for multi-turn moves where you charge on one turn and attack on the next i.e Solar Beam
3) Is there the ability for moves such as Protect where all damage is negated?
4) Is there the ability to force a move to go first e.g Quick attack
5) Is there the ability to force a critical hit?
6) Is there a way to force an attack to never miss i.e Swift
6) Is easy, just set the accuracy field to a really big number (all buff moves currently have 999 accuracy so they basically can't miss, though you might wanna go even further just to be sure).
1) There's no weather system currently. I'd probably implement this as a pair of global variables (weather type and remaining duration). At the end of every turn, count down the duration, and if it reaches zero, reset to "no weather" and display a message. Altering damage for certain types would be done in the script named compute_damage: simply check if e.g. it's raining and you use a water or electric move, and if so, change the multiplier variable accordingly. Effects like sandstorms/hailstorms dealing damage could be put in the same place ailment damage happens.
2) The easiest way to do this would be to implement a new Side Effect, I guess: the move itself does nothing, but has a 100% chance to add a "readied move" status which makes you skip the next turn's action assignment, and automatically enqueue the readied move (similar to how Sleep/Paralysis works), which would be special normally-unlearnable moves that unleashes the actual attack. These could be instance-variables in the battlemonster objects instead of something you store in the AMP data, similar to the buff array.
4) This is pretty straightforward, currently moves are added to a priority queue (with action_slate) using purely the monster's speed as the sorting factor. You'd add a new field to move data for "priority", which would be 0 for most moves but 1 for moves that strikes first; add a large number which will exceed the max possible monster speed stat (like 1000) times the selected move's priority value to the sorting factor when slating a move. (Also note you could give some moves negative priority to always happen last once this system is in place)
You'll probably need to increase BATTLEACTION_SPEEDBONUS_ITEM to 10,000 and BATTLEACTION_SPEEDBONUS_SWITCH to 100,000 while doing this, so high-priority moves can't happen before an item use or switch.
3) This would be a combination of the solutions to #2 and #4: give the protect move a high priority value (I think Pokémon gives protective moves 6 priority while Fake Out has 3, Extremespeed 2 and all other first-strike moves 1?) and make it apply a special 1-turn side effect (which is cleared when the turn ends) which just overrides the accuracy check ("if(random(100) <= acc-eva){" in obj_battlecontrol's Step event) - i.e., you check for the "protected" status (another instance variable for battlemonster objects) and if true, you enqueue a priority action that displays a message that the move failed due to protection rather than doing the regular hit chance check.
5) Currently there aren't even critical hits in the game, so I guess the easiest way to do this would be to add a "crit%" field to the move data and do a separate crit roll in compute_damage, returning an additional value, true/false depending on if it was a crit or not - this would affect the message and potentially sounds/visual effects... you'd also update the damage value accordingly, since crits could ignore defense... so you'd need to do this check early on. I think the easiest place to do this would be right before the "read level" segment: do a crit roll based on the new field (global.move_data[move,mvd_CRITCHANCE]) and if true, set def_buff to min(def_buff,1) and multiply atk_stat by 2. (And to make a crit guaranteed, set the crit chance to a number greater than 100)
Awesome, thanks for the in detail response there, the support you're giving is top notch.
As an aside, do you have any plan to implement any new features to the framework at any point, or is what is offered now the final product from you? More curious than anything, what you've already provided is an amazing product for games like this to get off to a great start so I'm very happy with the purchase :)
I consider it done and final - the way Game Maker projects works, it would be really hard for people to just import the new features into their existing games, anyway, so I'd actively give people a problem if I messed around with it too much. If a new GM change completely breaks it, I'll release bugfixes, though. (array_length_1d is used all over the engine but has been deprecated, for instance, so that might become an issue at some point... easy to fix once you know about it, but I don't wanna force my paying customers to go through that just because I'm lazy!)
Okay, I understand and it makes complete sense. Thanks.
I just want to leave a big thank you. My last comment was from 2 years ago when I bought this and I've learned so much from the code. I don't use it for any (active) project, but I learned a lot about viable database structures. Keep up the good work
Glad it could be of some use! ^__^
I see all of these engines as learning tools (after all, once you get better at coding any pre-existing code tends to feel restrictive and annoying to deal with) but I don't usually get to see people learning so much they can leave the proverbial nest and spread their wings! Thanks for coming back and telling me about it, it warms my heart :)
Is there a script / function that I can easily call which adds a mon to the party? I'd also like to set it up so 2v2 is the standard, how do I go about that?
Not 100% sure what you mean with "add a mon to the party", is it a new mon or one from a box?
If the former, check out how mev_intro_starterselect_confirm does it: first get the first free AMP (=Active Monster Party) slot in the player party region, then generate a new monster there. The latter (swap out a pre-existing monster) is a bit trickier but there's code for this in mev_terminal_interact.
For multi battles, the important thing is setting the two variables global.player_side_monsters and global.enemy_side_monsters to the amount you want on each side (in your case both would be 2). Check out the scripts cc_battlestart_trainer and player_step_fill for trainer and random encounters respectively.
A new mon, like at the start of the game instead of the selector or a gift from an NPC for example, something like GetNewMon(monster) and it'd just go off and add it to the party if it can or a box if not, but I'll look into what you've said and go from there. And I'll look into the global vars, appreciate the response. Great framework tho, I've had to do some bits for it to work at a smaller res but that was simple enough once I'd read through the code a bit.
Another question, I'd like to give items in the inventory little icons. I was thinking that I could just add this info into the init_item and then add some code to draw the icon sprite within the inventory menu, any reason this wouldn't work?
The code when a monster is caught should do basically what you want, then (find the first free slot in the party or box structure, and then copy the monster data from the enemy section to there).
And yeah, your line of thinking for item icons is exactly what I'd recommend! Just make sure to use a "scrollable sprite" type when coding the new menu element, so it fetches data relative to the current cursor position - basic "sprite" would essentially hard-code the sprite. (All the name strings in the inventory does this too)
Awesome thank you. Another couple of questions:
1) where is the flashing white cursor code located? I can't for the life of me find that.
2) Is there a 'delete file' feature already created or any other save file management implemented aside from saving and loading? (like overwrite or multiple saves from the same file etc)
Also, if there is a better means for me to ask questions/seek support then let me know :)
Itchio messages are my preferred method of communication, it's nice when the questions (and answers!) are located next to the game for assets in particular and I check my messages at least once every day (unless something really serious gets in the way).
1) The flashing white rectangle that's overlayed on top of the highlighted menu option? It's in the script ggui_draw. It's actually drawing a rectangle with vector drawing instead of drawing an asset.
2) No. I'm kinda scared of implementing stuff like this because I always worry they'll go wrong and delete someone's progress, so I prefer leaving the responsibility to the player's operating system.
Hi, this looks really good and I've played the demo for a bit and I just had a few questions for if certain functionality is included and if it isn't is there plans for it or would the engine be extensible enough to include it with some work my side.
- item to teach a move to a monster
- more than 4 moves per monster
- Abilities, special effects that each monster possesses (passive skills basically)
Thank you
None of those are included by default, but since this is just a Game Maker project it wouldn't be impossible to include them.
Ah I see. Thanks very much for getting back to me so soon, going to give this a whirl over the weekend 👍
You can make good top-down racing engine. If all your engines are like this, we will be happy to buy the ones that are potentially useful to us. It will speed up our work on new titles. In the end, there will be little of them, but at the beginning it will be a great help.