Skip to main content

News

Topic: Generalized random battle implementation? (Read 3594 times) previous topic - next topic

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Generalized random battle implementation?
So I want to do a generic implementation of random battle triggering that doesn't require the checks to be copy/pasted into every single map script.  The thing is though, I'm not sure how to approach this.  Essentially what I want is to be able to do this once:
Code: (javascript) [Select]
RandomBattles.attach("Player");


And then in each map script:
Code: (javascript) [Select]
RandomBattles.setEnemies('lumisquirrel', 'ghostThing' /*[...]*/)

And from then on have it continue to work no matter what other maps the player moves to.
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Generalized random battle implementation?
Reply #1
I'm sure you have tried persist/analogue? Right?

You can create a global handler, a singleton like so:

Code: (javascript) [Select]

var RandomBattles = (function() {
    var player = "", enemies = [];

    function Attach() { /* ... */ }

    /* ... */

    return {
        setEnemies: SetEnemies,
        attach: Attach
    }
})();


Then in a persist map script:

Code: (javascript) [Select]

{
    enter: function() {
        RandomBattles.setEnemies(/* ... */);
    }
}


And if you don't call RandomBattles on subsequent maps it'll just continue to use the current setup.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Generalized random battle implementation?
Reply #2
Yes, I know how the framework would look, what I mean is, how do I do the actual attachment?  As in, where would I put the code to check for a battle and trigger it so that I don't have to code that into each individual map script?  The Player entity persists across map changes, so...
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • N E O
  • [*][*][*][*][*]
  • Administrator
  • Senior Administrator
Re: Generalized random battle implementation?
Reply #3
I'm going to be honest here: I don't understand what's being asked.

If the question translates to "how to trigger a random battle," this is primarily why trigger functionality (but more importantly, zones) was implemented in maps; you enter a zone and while in the zone a script runs (e.g. get a random number if at least 100ms has passed since the last random number, and if it equals 5 start a battle) or you enter a trigger and a script runs once (e.g. set a counter to a random number, then after that many steps are taken outside of the trigger start a battle).

If the question translates to "how to set up a specific set of battles that are going to be triggered for this specific map," then I gather the solution is similar to the above posts but then needs to be triggered using map zones/triggers or equivalent scripted functionality.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Generalized random battle implementation?
Reply #4
Triggers or zones.  That's what I figured.  Which requires the actual code that tests for a battle to be put into each map's script.  Obviously analogue.js makes that task easier, but it's still code duplication, which I wanted to avoid.  What I want to do is to be able to initialize my RandomBattles singleton once, and have the logic follow the player around regardless of what map they go to.  Literally the only code that I want to have to put into a map script is when the map is loaded, to set the enemies that can appear and various area-specific parameters like encounter rate, etc.  The RandomBattles singleton should ideally handle all the testing for a battle and triggering the battle engine itself without any additional code in the map script.  Obviously things like zones can be used to customize encounter rate, etc. for different parts of a map, but I don't want to have to make triggers on every single map to test for a battle.

In pseudocode, what I want is this:

As part of one-time initialization:
Code: (javascript) [Select]
CreatePerson("Player", "hero.rss", false); // persistent entity representing player's sprite
RandomBattles.initialize();
RandomBattles.attach("Player");


And then on map load:
Code: (javascript) [Select]
RandomBattles.setEnemies( /*...*/ );
RandomBattles.setEncounterRate(whatever);


Long story short, I want the RandomBattles object to handle all the battle testing, etc., after just a single call to .attach() and no other code required in a map script other than the initialization listed above.  Is this possible?
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Generalized random battle implementation?
Reply #5
Oh! You want to automatically fill certain zones and triggers with random battle code to cut down on coding.

I see it now.

Code: (javascript) [Select]

var z = GetNumZones();
for (var i = 0; i < z; ++i) {
    SetZoneScript(i, 'RandomBattles.execute()');
}


Trouble is there is no way to set zone scripts. You can set other properties and execute zones but I think you can only set the script in the editor.

Next idea: Tile names. This is what I did this for such a thing you are asking.

Name a tile 'battle'. Then in the update script add a 'listener'.

Code: (javascript) [Select]

function Update() {
    if (GetTileName(GetTileAtPlayer()) == 'battle') {
        RandomBattles.execute();
    }
}


On tile based movement games you'd do the check per tile move. Otherwise it gets a bit trickier. The performance oft his is minimal since you are only getting and checking coordinates per cycle. I used this trick in Pokémon games back in the day.

Naming a tile is simple since wherever you paint that tile will have that battle attached. You can even do customizations such as 'battleA' and 'battleB', to differentiate between ground or water types.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Generalized random battle implementation?
Reply #6
Yeah, that's basically it: I want the random battle logic to be tied to the movement of the player's character, leaving the only responsibility of the map-load/zone scripts being to set the specific parameters for that area.  Basically a "set it and forget it" deal, so that every single map doesn't have to duplicate the battle-triggering code.  Obviously it can be reduced to a single function call (RandomBattles.step() or similar) but even that's a bit too dirty and code duplicate-y for my tastes.

The named tiles I didn't know about though, and that may indeed come in handy.  My first idea was named zones, but apparently Sphere doesn't support naming zones for some reason... but the tile naming does seem like the next best thing, so thanks for the tip!
  • Last Edit: September 28, 2014, 11:39:05 pm by Lord English
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Generalized random battle implementation?
Reply #7
Well, the named-tiles method would work, except for one little issue: Putting the check into the update chain means a battle could start at any time, even if the player is standing still.  I only want to generate a check when the player moves.  And since I really don't want to go full tile-based ala Pokemon... yeah.  Not sure what to do here.  I really don't want to have to write a custom map engine, especially since that means losing access to Sphere Studio's nice editor.

How often is a trigger/zone script called?  Only once when the player enters the zone, or continuously while the character moves around in it?  If it's the latter I could probably work with it, but if not...
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Generalized random battle implementation?
Reply #8
Are you afraid of detecting movement? It's not so bad, but you'd have to decrease the frequency by making the chance per-move really small.

Code: (javascript) [Select]

function Update() {
    if (!IsCommandQueueEmpty(player) && GetTileName(GetTileAtPlayer()) == 'battle') {
        RandomBattles.execute();
    }
}


The tile base approach works better on tile movement engines I say.

Triggers: they trigger once when you enter the tile. They do not retrigger even if you move around in that tile. They reset when you leave that tile. There is a bug, however: a 1px border to the top and right of each trigger is a dead zone. It is possible to walk between two triggers without triggering either of them. I notice this infrequently in Blockman when there is a 2 tile warp. Zones alleviate that.

Zones are triggered when you take 'n' number of steps in them. They do nothing if you are idle. They are indeed the backbone to FBNil's lithonite terrain engine. But they are just as clunky as triggers and you'll be doing a lot of zone maintenance. Fortunately my Sphere editor makes zones easier to work with.

If I were you I'd still try to use tile names. But that's just me. It really puts the effort to 0 if certain tiles are for encounters. Heck you can even make a layer with red tiles painted on it - these are the 'tile triggers' and you hide the layer from view. Then in code you still check against it. The tiles are still accessible in code even if they are hidden on the map. I could mock out a demo if you want.
  • Last Edit: October 01, 2014, 09:04:54 pm by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Generalized random battle implementation?
Reply #9
Code: (javascript) [Select]

function Update() {
    if (!IsCommandQueueEmpty(player) && GetTileName(GetTileAtPlayer()) == 'battle') {
        RandomBattles.execute();
    }
}


But wouldn't that check for a battle every single frame as long the PC is standing still?  I tried to implement the movement detection using GetPersonX/Y like this:
Code: (javascript) [Select]
Scrambler.prototype.update = function()
{
var x = GetPersonX(this.personID);
var y = GetPersonY(this.personID);
if ((x != this.lastX || y != this.lastY) && IsCommandQueueEmpty(this.personID)
    && RNG.chance(this.encounterRate))
{
var battleID = RNG.fromArray(this.battleIDs);
new Scenario()
.battle(battleID)
.run(true);
}
this.lastX = GetPersonX(this.personID);
this.lastY = GetPersonY(this.personID);
};


...but for some reason, (x != this.lastX || y != this.lastY) is always evaluating to false, even when moving the PC around, so no battle is ever triggered, and I'm hard-pressed to understand why.
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Generalized random battle implementation?
Reply #10

But wouldn't that check for a battle every single frame as long the PC is standing still?


Not unless standing still also means commands are being sent. As long as the queue is not empty does it check, and the queue is usually not empty when you are moving. But I like your other approach better since there's more control over it.

Now, about that other method:

I tried to implement the movement detection using GetPersonX/Y like this:
Code: (javascript) [Select]
Scrambler.prototype.update = function()
{
var x = GetPersonX(this.personID);
var y = GetPersonY(this.personID);
if ((x != this.lastX || y != this.lastY) && IsCommandQueueEmpty(this.personID)
    && RNG.chance(this.encounterRate))
{
var battleID = RNG.fromArray(this.battleIDs);
new Scenario()
.battle(battleID)
.run(true);
}
this.lastX = GetPersonX(this.personID);
this.lastY = GetPersonY(this.personID);
};


...but for some reason, (x != this.lastX || y != this.lastY) is always evaluating to false, even when moving the PC around, so no battle is ever triggered, and I'm hard-pressed to understand why.


The code does that since the action happens once per frame, and not once you move, so it might be a syncing issue. Have you tried putting the lastX/Y inside of the if (and setting this.lastX/Y to something other than the start position for that character)?

Edit: maybe something like this?
Code: (javascript) [Select]
Scrambler.prototype.update = function()
{
var x = GetPersonX(this.personID);
var y = GetPersonY(this.personID);
if (x != this.lastX || y != this.lastY) {
this.lastX = GetPersonX(this.personID);
this.lastY = GetPersonY(this.personID);
                if (IsCommandQueueEmpty(this.personID) && RNG.chance(this.encounterRate)) {
var battleID = RNG.fromArray(this.battleIDs);
(new Scenario()).battle(battleID).run(true);
}
}
};
  • Last Edit: October 07, 2014, 08:33:58 pm by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Generalized random battle implementation?
Reply #11
Oh, I see it now: The condition is if the command queue is NOT empty, somehow I missed the exclamation point when I read it.  Don't I feel stupid now.

Also, speaking of feeling stupid, I figured out my issue.  It turns out the code I wrote works fine: The only reason it wasn't triggering any battles was because Scrambler:update() didn't return anything, causing the thread to terminate after the first frame (a return value of false or equivalent ends the thread).  How embarrassing!  The worst part of it all is that this isn't the first time I've done this.  And every single time it takes me a few hours of debugging to figure out what went wrong! :-[
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Generalized random battle implementation?
Reply #12

Oh! You want to automatically fill certain zones and triggers with random battle code to cut down on coding.

I see it now.

Code: (javascript) [Select]

var z = GetNumZones();
for (var i = 0; i < z; ++i) {
    SetZoneScript(i, 'RandomBattles.execute()');
}



I'm glad I found this thread again, I'm going to have to implement the above (SetZoneScript) in minisphere now. :)
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan