Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs

Yal

373
Posts
83
Topics
999
Followers
24
Following
A member registered Aug 12, 2015 · View creator page →

Creator of

Recent community posts

I'm aware collision checking is super buggy, it's definitely one of those things I'll need to work on for the post-jam version... right now I mostly tried to hide the issues by adding grace ranges and designing levels to avoid causing the edge cases, but there's a bunch of inconsistencies where the same action randomly succeeds or fails depending on what the maths feels like. I rolled with Game Maker's built-in collision system to speed up development, but it'd probably make sense to have my own grid-based approach for more control (rooms only really move in tile-sized increments anyway)

Glad you enjoyed the game despite the issues though! ^__^

Like 30% of the development time was trying to fix bugs with the collision checking, no joke! For some reason I ended up making the base room 3x3 tiles big (with separate edge and middle tiles), turns out that meant there's a lot of division-by-3 shenanigans going on everywhere that makes the maths break down when calculating if a pixel is occupied or not...

The jam time budget didn't allow for applying makeup, I'm sorry. You could always try blurring your monitor to make the wrinkles harder to spot.

I tried out the changes myself and it works for me, with the one small change I mentioned:

var anilen = global.skelani_animation_length[atkanimation] - 1;

(It's a bit tricky to see since the animation is only 5 frames long, but you can press "Home" to lower the framerate to 10/s)


So the full, working script looks like this:

/// @description psm_melee()
function psm_melee() {
    var anilen = global.skelani_animation_length[atkanimation] - 1;
    //Physics
    if(onground){
        player_naturalfriction()
    }
    player_controls_jump() //Can jump-cancel
    player_inertia_x()
    player_inertia_y()
    player_boundaryclamp()
    //Animate, spawn attack
    statecntr++
    if(statecntr < atkwarmup){
        skelani_set_animation(atkanimation,0)
    }
    else if(statecntr < atkwarmup + atkduration){
        skelani_set_animation(atkanimation,anilen*(statecntr-atkwarmup)/atkduration)
    }
    else if(statecntr == atkwarmup + atkduration){
        skelani_set_animation(atkanimation,anilen)
        n = instance_create(x,y,atkhitboxobj)
        n.image_xscale = skelani_xscale
        n.sprite_index = atkhitboxspr
        with(n){
            player_weapon_get_stat_block_current()
        
            //Heavy attacks are 30% stronger, but only for melee weaposn, so this code is only here.
            if(other.atk_isheavy){
                rpg_adjust_atk_stat_block_uniformly(1.3,0)
            }
        
            //When winded, lower power of attack
            if(other.atk_winded){
                rpg_adjust_atk_stat_block_uniformly(0.7,0)
            }
        
            //When wielding a weapon without proper stats, power is reduced greatly.
            var str, dex, int, fth;
            str = db_read_stat(itemtype_WEAPON,other.atk_weapon,wd_REQUIREMENT_STR)
            dex = db_read_stat(itemtype_WEAPON,other.atk_weapon,wd_REQUIREMENT_DEX)
            int = db_read_stat(itemtype_WEAPON,other.atk_weapon,wd_REQUIREMENT_INT)
            fth = db_read_stat(itemtype_WEAPON,other.atk_weapon,wd_REQUIREMENT_FTH)
            if(!player_meets_requirements(str,dex,int,fth)){
                rpg_adjust_atk_stat_block_uniformly(0.1,0)
            }
        }
        n.lifetime      = atkduration
        n.blockbreaker  = player_weapon_get_stat(wd_BLOCKBREAKER)
    }
    else if(statecntr < atkwarmup + atkduration + atkcooldown){
        skelani_set_animation(atkanimation,anilen)
    }
    else{
        state = psm_ordinary
    }
    //Handle animation with correct hand
    player_animate_offhandswap()
    //Kneel down when crouching
    if(crouching){
        skelani_override_crouch()
    }
}

Open the script init_monsters and scroll to the right a bit, and you'll find this block of code:


Edit that :)

(Moves are pairs [LearnLevel, MoveID] and a learn level of NONE means the monster always starts with it known)

Okay, to recap in a bit more structured way: the skelani_set_animation(atkanimation,(statecntr-atkwarmup)/atkduration) line should be multiplied with the number of frames in the animation, and any skelani_set_animation(atkanimation,1) should instead set it to the final frame.

It might be easier to do these changes if you add this line right below "statecntr++" to read out the length to a variable:

var anilen = global.skelani_animation_length[atkanimation];

Then use anilen as the length in the code:

skelani_set_animation(atkanimation,anilen) //Originally ended in "1"
skelani_set_animation(atkanimation,anilen*(statecntr-atkwarmup)/atkduration) //The interpolating one

If it acts weird, try setting anilen to global.skelani_animation_length[atkanimation] - 1 instead.

Oh wait, I'm stupid... you should use atkanimation instead of argument0 when reading the animation length (I was looking at skelani_set_animation when giving you advice for psm_melee). psm_melee has no arguments so argument0 is undefined when it's run, the attack animation in use is in the atkanimation variable.

(So replace the global.skelani_animation_length[argument0] with global.skelani_animation_length[atkanimation] everywhere in psm_melee and it should work)

The attack animation in psm_melee uses skelani_set_animation to explicitly set the animation frame instead of playing it automatically, this is why you don't see the new frame at the end play out. Since all the default attack animations have 2 frames, the hardcoded 0 and 1 will work, but one thing you could do if you want more intricate animations is to use global.skelani_animation_length[argument0] instead of 1 when setting the animation, and for the line that goes

skelani_set_animation(atkanimation,(statecntr-atkwarmup)/atkduration)

you'd instead use

skelani_set_animation(atkanimation,global.skelani_animation_length[argument0]*(statecntr-atkwarmup)/atkduration)

(which means you interpolate between all the frames instead of just the first two)

Since indexing starts at 0 you might need to use (global.skelani_animation_length[argument0]-1) instead of just global.skelani_animation_length[argument0] to prevent the animation from looping back to the first keyframe at the end, I don't remember if the length is inclusive or not.

Oh yeah, you might need to change the viewport settings for every room as well... I forgot they're set in GM's room editor and not by code. (There's just 7 rooms so it's not super involved). Both the "port on screen" and "view in room" values should have width/height matching your new resolution. obj_darknesscontrol uses the view size and not the VIEW_W/VIEW_H macros so this should fix that as well (the same applies to obj_control's view and deactivation code).

(1 edit)

I'm pretty sure updating the VIEW_W / VIEW_H macros should make everything else "just work" since they're used everywhere, though GUI elements that adapt to the size might get too big and sparse and elements that don't might get small and hard to read... so you probably need some adjustments for visual style (but the CODE bit should work without changes at least, unless I messed something up). Most of the frame-based menus have coordinates in percent of the frame for all elements, so just changing the size of the frame everything's stored in should automagically resize the elements.

Maps might be broken since the map grid size is based on the number of screens the room occupies (which changes if you scale up the game to have a bigger screen), but I assume you're going to remake all levels from scratch so hopefully this isn't going to be an issue.

(1 edit)

I had a look and it turns out the editor has the lengths hardcoded in its Create event (hit Ctrl+T and start typing "obj_editor_skeletonanimation" and it'll take you to it), it also uses a set completely different variables instead of normal skelani ones... but you could solve both of those issues at once by, at the end of its Create Event, first calling skelani_init, then your updated skelani_init_limb_lengths_humanoid, and finally a loop that just sets all the sklen variables to values of their skelani_length counterparts.

Nothing built-in, sadly. The approach I'd recommend (which is from a more recent project) would probably be turning keys into device/ID pairs like this...



..and then in input_get, have a switch statement that reads the appropriate device based on the first member, using the second as an ID:


(Note that the code above uses a bunch of GMS2-isms and will not work in the GMS1 version of the engine)

Thanks, glad you like the engine! Looking forward to see what you'll make with it! ^__^

One of my first (failed) GM projects back in the early 2000s was making a game in this style, but it just failed miserably... it felt great to finally be good enough at coding to tackle it :P

Thanks for the patronage and interest in the engine! ^__^

That's quite a lot of questions! I'll try to answer them all in order:

  • To scale sprites up, the most important thing is increasing the limb lengths (these things are unrelated to the sprite sizes to allow for things like pauldrons that cover more than the entire body part). Check out skelani_init_limb_lengths_humanoid; you'd either edit this directly (if you want everything to scale up) or make a new script (if you only want some things, e.g. giant ogres, to scale up). A search for the script's name should reveal all places it's used.
  • If you just edit VIEW_W and VIEW_H (in the "macros" script) to match the new resolution, everything should just magically work. (Some UI elements might need resizing since they look too small or too big, depending on the new resolution, but they should adapt just fine without additional work).
  • The easiest way to have an in-engine name-entering dialog is to just have a GGUI menu for it, which is a grid with all the letters in it. All events are the same script, which just adds the corresponding letter to the global name string, except for the "DONE", "ERASE" and "CANCEL" events of course. (Obviously you'd want a loop that creates all the entries instead of manually defining every letter separately)
  • To make the player's weapon aim towards the mouse cursor, you can just manually override the skelani_drawangle of the weapon's limb after you've updated the animation, using the direction to the mouse cursor. (You might want to also update the corresponding arm's angles). The player_skelani_override_guns script should give you an idea for how to do this, it's basically exactly what you want except it's not mouse-controlled.

Sadly no, I'm shorter on time than money these days and doing commissions would not improve the balance in the direction I want. But if you just want some simple questions answered, ask them here and I'll answer them (I try to check my Itchio notifications at least once per day).

(1 edit)

I have absolutely no idea, I don't have a mac and I don't have any intention of getting one any time soon. But none of the functions I've used are Windows-specific so it probably should just automagically work thanks to the GM sandboxing? (I'm really careful with those edge cases that breaks compatibility these days)

The one thing that could be an issue is the special rendering trick used to make you sink halfway through tall grass, but you could work around that by using draw_sprite_part and only draw half the player when you're in tall grass.

Not all shields has 100% block rate against all types of damage (you can see this if you check their status screen) so you usually still take some chip damage. Of course you can change this behavior in a game made with the engine, either by changing the code that handles blocking or just give shields 100% absorption for all elements when creating your item database.

(1 edit)

In my experiences, the easiest way to handle jumpthroughs is something like this:

  • If you're moving upwards (yspeed < 0), do nothing
  • Else, if current position is colliding with a jumpthrough, do nothing, because this means we're currently falling through one we didn't land on
  • Else, treat them as normal solid platforms and return true if the to-be-checked point is a jumpthrough.

checking the calling instance's current position doesn't quite belong in a general-purpose function like pixel_vacant so perhaps doing the check in-instance and then supplying a "check_jumpthroughs" argument to pixel_vacant would be cleaner - this argument would be set to !place_meeting(x,y,parent_jumpthroughterrain) && (yspeed >= 0)


The drawback with this approach is that you need a player-sized berth around jumpthroughs and can't stack them directly on top of each other vertically, but since they usually have pretty thin hitboxes to begin with (only the top) it's not usually an issue.


Oh, and if you want to be able to fall through jumpthrough platforms, you need to check if you're standing on one and pressing down+jump, if true, move the player down enough pixels that they're now colliding with the jumpthrough (at least 2 to account for rounding errors when checking the pixel directly below the player for ground) and now they should ignore the jumpthrough they're currently standing on.

The skelani_length array contains the lengths of each body part (there's also skelani_shoulderwide and skelani_hipwide that adjusts how far away from the spine the arms/legs are anchored).


For just a one-off character you could make a custom NPC Skelani Setup script that changes these values, if you want to change a lot of characters you might want to duplicate skelani_init_limb_lengths_humanoid and use that when setting up the smaller characters (or, edit it directly if you want every human to be smaller)

(1 edit)
  • IV = Individual Value, an unique potential for the monster that's generated on creation and never changes
  • EV = Experience Value, experience for that stat (which goes up over time as it's fighting)

Did you press Enter? The game uses Z / X for the accept/cancel buttons.

I kiiiiiiinda lost my original Soundcloud login years ago and figured it wasn't worth trying to find it again since I was so close to the upload cap and nobody listened to my stuff anyway. ^^'

All of the 8-bit and 32-bit packs has demos, though... scroll down to "download demo" and you should find zip files with a bunch of the songs from that music pack. (Some of the packs also has an embedded Youtube video showing off one of the songs... it's just 1 song but hopefully it's enough for a simple pass/fail check?)

Not really... the codebase is really old and uses hardcoded keyboard events in every game object, it'd be a gargantuan and error-prone task to replace them with something more flexible. Please use a third-party gamepad-to-keyboard-event application like Antimicro or Joy2Key instead.

Okay, I think I've found the cause: the liftable is colliding with itself (rather, its "idle prop") so we need a special exception for that. Something like this should work better:

var blocked = false;
with(parent_terrain){   if(place_meeting(x,y - 4,other)){     if(id != other.idleprop){       blocked = true;     }   } }
if(!blocked){   y += 4   with(idleprop){     y += 4   } }

liftable_free just checks if the position is free of collisions, you also need to move the object:

if(liftable_free(x,y + 4)){
  y += 4
}
(1 edit)

There's a variable state_idle which lets you change the idle behavior (what the object does when nobody is lifting it). Copy liftablestate_idle into a new script "liftablestate_idle_with_gravity", and then add some code for the gravity here (and then don't forget to update the liftables you want to have gravity when idle so they use this script for their idle state instead).

For the gravity code, the easiest would be to just check liftable_free(x,y+4) and if free, move down 4 pixels (constant speed instead of using actual gravity). If you want actual gravity, it's probably easiest to copy player_inertia_y's yspeed code and removing the "shockwave when hitting blocks" bit.

Did you change any of the TV effect / night effect / screen size options? What's your machine specs? The TV effect messes a bit with the draw pipeline and uses a lot of memory, and if you're low on VRAM it's possible for it to just never be able to draw to the screen at all. Try playing with the TV effect turned off if you think this could be the issue.

More or less exactly the same, but legally distinct! :3

It's set up in script player_locals_init, it is always set to one of these "playerstate_" scripts:


(You can quickly find assets by using Ctrl+T for the "go to anything" searcher or middle-clicking the script/asset name in the code editor)

You can check/change the controls from "Keyboard setup" or "Gamepad setup" in the main menu. (The blank key for using items in the default controls is spacebar)

Hmm, Game Maker's linux port only supports Ubuntu out of the box but there's not a whole lot of Ubuntu-specific library requirements. And libcrypto is so common it should be installed. What is the output of ls /usr/local/lib | grep crypto ? If you have some libcrypto.so file, but not an "1.0.0", you could solve this by creating a symlink named "libcrypto.so.1.0.0" which links to the libcrypto SO you actually have.

While you're at it, you might also want to verify that the other library requirements are installed: libcurl4, libopenal1, joystick, jstest-gtk.

Glad you could get it working!

I'm probably not going to release new engines in the near future, I've basically done everything at this point :P But we'll see what happens once 3D support gets better...

(1 edit)

I looked into this, the line that causes the issue is line 24 of ev_batville_veggiscene:

gml_Script_ev_batville_veggiescene (line 24) -        csc_q(prompt_add_alt("Like chicken, I guess...",0010))

The underlying problem is that csc_q tries to deactivate the object that is returned, but prompt_add_alt doesn't return an object. I think modifying it like this is all you need to fix the issue:


(in previous versions of Game Maker scripts would return 0 if no return value was specified, which is a valid ID for instance_deactivate functions even if it's never actually used as an instance ID)

(1 edit)

Glad the project has been useful to somebody! ^__^

For #1, the easiest option would be to have the stone type matchup affect catch rate directly. In init_items, change the argument for all monster stones to an array with two elements: catch rate, and a new type affinity parameter. Then in itemuse_catch, change line 13 from "argument1" to "argument1[0]" (reading the catch rate from the array) and add a new parameter for the catch rate as a multiplier for the final catch rate. For instance maybe you could write a function that, given an AMP ID and a type, returns either 1.00 if the monster has that type, or 0.05 (or even zero) if it doesn't, and then have the type parameter just be the elemental type that stone works on. (Or the factor could be based on something else entirely...)

Blocking the player from using the stone entirely if the monster doesn't have the type gets a slight bit more complicated, I think the easiest incision point would be in mev_battle_item_select. After you get the list of valid targets "targets" at line 6, go through the list and remove all targets whose type the stone doesn't work on, using whatever logic you are planning: hardcoded whitelists, type checks, level checks etc. (The selected item is in "my_item" at this point so you can access its data). Keep in mind, the list is an array and not a ds_list.

For #2, the GGUI code is so deeply ingrained in the menu functionality that it's probably going to be pretty difficult to replace it entirely. What exactly are you planning to do? It's made to be as generic as possible (with elements like frames and sprites that can use any assets you want) so it should be possible to script more or less anything with it. You can also add new element types by editing ggui_draw.

Yes, of course!

(1 edit)

To evolve monsters, you need to take the following steps:

1) Store the player's current position (not necessary, but if you want them to be exactly where they were when you come back after the evolution, you should do this)

with(obj_player){
        global.load_x            = x
        global.load_y            = y
        global.load_room        = room
        global.load_direction    = drawdir
}

2) Put the monster in the evolution queue:

ds_queue_enqueue(global.pending_evolutions_queue,[monamp,species]);

monamp = AMP index of monster to evolve - when you do a batch operation, you should loop over the AMP indexes you want to affect (e.g. entire player's party is from 0 to PARTYSIZE_ACTIVE; party + boxes is from 0 to PARTYSIZE_ACTIVE + PARTYSIZE_BOXED)

species = monster species to evolve into (the global array global.monster_evolution_mon has all the defined evolutions, but you can force a monster to evolve into any other species if you want)


3) Finally, go to the evolution room:

room_goto_fade_dontdestroy(rm_evolution,60);
(1 edit)

AMP = Active Monster Party (aka, monsters that are currently loaded into the player party, an enemy party, or a storage box - it's different from the data about a monster species in general)

Nope, you can go as high as you want. The menus resize the sprites so they'll fit in the allotted space, but it doesn't happen in battle.

It's the same as != . I suppose it's supposed to mean "greater than or smaller" (aka everything except equality). I started using it after seeing it in some official Game Maker code and thinking it looked cool, but stopped some time later after realizing that no other language uses it and it just confused people.

You'll just have to wait ~11 hours and see ;)