Real time battle system attack bug?

RedFawkes

Veteran
Veteran
Joined
Jun 21, 2018
Messages
39
Reaction score
16
First Language
BR Portuguese
Primarily Uses
RMVXA
My project is heavily based on SAS - Sapphire Action System. I recently found a way to make enemies only be able to attack under the condition that a specific switch is on.
Here is the relevant event page (click to enlarge):
1595088715414.png
This is a parallel-process common event that manages enemy AI. In this page you can see the process of an enemy attack. There's a windup where the enemy shows the player that it's about to attack: they show a balloon, step backwards and wait a bit; and there's an attack, where the enemy changes its graphic (right now it's only a red tint over them) and charge forward.
When the windup process ends, the page checks if there's any enemy currently attacking. If there aren't, the game will Turn On a global switch that allows enemies to deal damage when touching the player and will turn On a switch that indicates there's an enemy attacking. If there are enemies attacking, the page will simply disable both switches - to try and guarantee that the player does not receive damage from enemy that is touching him but isn't in the "attack" state.
I say try because it doesn't work all the time. Check the video below, a slowed down fight highlighting the problem - note that the first two charges are correctly processed since the criteria is met (enemy is red and their front is touching the player):

At around 00:16 an enemy is able to hit the player even though he is not in the "attack" state (he's not red - he's simply walking towards the player. I suspect this happens because the second enemy activates the "enemies can attack" switch right when the first one finishes his charge, thus allowing the first one to hit the player when he shouldn't.

Is there an ingenious eventing way to fix this problem? Or even a way to script a solution? I run a slightly modified version of SAS made for the project and I could handle it to anyone interested in finding a script solution - since I'm no expert, but can handle the basics.

Any help is appreciated. Thanks.
 

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
61
Reaction score
18
First Language
English
Primarily Uses
RMVXA
I don't know anything about SAS, but I think your diagnosis is correct. Because you're using a global switch to indicate damage can be dealt and (it sounds like?) damage is dealt on intersection with the player's hit box, then any monster intersecting the player when any (potentially other) monster is in the attacks state will result in player damage.

I'm not sure I can offer much help about SAS, although my initial thinking would be to use self switches rather than a global switch to indicate that a monster can deal damage. If that isn't possible, can you modify monster stats to prevent attacks from being effective? So if you can't make the wrong monster not attack, can you make it deal zero damage (or better) have a 0% hit rate? Maybe if you talk a bit more about how SAS determines when an attack hits, I could help more... I downloaded it to look, but it seems quite expansive. So a quick tutorial might be faster than reading up on how it works.
 

MushroomCake28

KAMO Studio
Global Mod
Joined
Nov 18, 2015
Messages
3,617
Reaction score
4,618
First Language
English
Primarily Uses
RMMV

I've moved this thread to RGSS Script Support. Please be sure to post your threads in the correct forum next time. Thank you.

 

RedFawkes

Veteran
Veteran
Joined
Jun 21, 2018
Messages
39
Reaction score
16
First Language
BR Portuguese
Primarily Uses
RMVXA
I don't know anything about SAS, but I think your diagnosis is correct. Because you're using a global switch to indicate damage can be dealt and (it sounds like?) damage is dealt on intersection with the player's hit box, then any monster intersecting the player when any (potentially other) monster is in the attacks state will result in player damage.

I'm not sure I can offer much help about SAS, although my initial thinking would be to use self switches rather than a global switch to indicate that a monster can deal damage. If that isn't possible, can you modify monster stats to prevent attacks from being effective? So if you can't make the wrong monster not attack, can you make it deal zero damage (or better) have a 0% hit rate? Maybe if you talk a bit more about how SAS determines when an attack hits, I could help more... I downloaded it to look, but it seems quite expansive. So a quick tutorial might be faster than reading up on how it works.
The thing is, I don't know exactly how to manipulate enemy data in SAS, or how it's handled in the script. I use a global switch (not ideal) because by messing with the script I found out that the code executes enemy attacks at line ~1658 (not exact bc my SAS has some additional code) with "if $game_player.pixel_range?(tx,ty)" then it executes the enemy attack. What I did was add a switch condition to it, so it's "if $game_player.pixel_range?(tx,ty) and $game_switches[EnmAtk::Switch]", now it asks if the pixels touch the player and if this switch is On. The game then deactivates and reactivates the switch as the enemies execute their attack process.
Self switches are out of question because i depend on them to make the combat work, like: Self Switch A ON = Enemy close enough to the player = Enemy attacks player. It also affect things like "forms" (such as boss phase 1 dies and activates Self Switch C which executes phase 2, things like that) and their death state.
I have no idea on how to alter enemy hit rate or damage individually.
The problem would be solved if only there was a way to individually check the enemies' ability to attack. I just don't know how to do it.
The enemy attack is written at around ~1650 and player attack is written at around ~1450, that's where it determines the hit.

Thank you for you help!
 

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
61
Reaction score
18
First Language
English
Primarily Uses
RMVXA
Sorry I was offline for a while ;) Nothing scary, just busy.

So, at first blush, what I would recommend is using a self switch instead. First off, I have made these utility functions on Game_Event:
Ruby:
  def setSelfSwitch(switch, value)
    $game_self_switches[[$game_map.map_id, @id, switch]] = value
  end

  def getSelfSwitch(switch)
    return $game_self_switches[[$game_map.map_id, @id, switch]]
  end
So you can just write event.setSelfSwitch('A', true), which is a lot easier than the above. Another interesting fact you might notice is that the underlying self switch mechanism doesn't limit itself to just four. You can in fact do this:

1596128207998.png

And it'll print 'On!'. Obviously, you cannot access these 'extra' self switches for activating event pages (just b/c RPGMaker UI doesn't support it... but I know a way to work around that - it's just a bit of code - if it's critical), but you absolutely can use them in conditions via a script call.

If you like thisEvent, it's just this (BTW):
Ruby:
  def thisEvent()
    return get_character(0)
  end
I find that thisEvent reads a lot more cleanly and is easier to type than get_character(0).

I think with that, you should be able to modify your original code to use self switches rather than a global switch (it looks like it is running in the context of the attacking event, so just using thisEvent should work). And the SAS script you were modifying (the attack function) is also running in the Game_Event, so you can use self.getSelfSwitch there too. So, <fingers-crossed>, that should allow you to use self switches.

I will note that all self switches for the entire game are stored in the same file. So don't spawn tens of thousands of them per event... but having 10 per event in the entire game is unlikely to make any real difference for perf or file size unless your game is utterly massive.
 

RedFawkes

Veteran
Veteran
Joined
Jun 21, 2018
Messages
39
Reaction score
16
First Language
BR Portuguese
Primarily Uses
RMVXA
This worked! You won't believe how extraordinarily useful this has been for the project. Thank you!
 

kyonides

Reforged is laughable
Veteran
Joined
Nov 17, 2019
Messages
287
Reaction score
71
First Language
English
Primarily Uses
RMXP
I'm not against OOP here but that method call is quite unnecessary.
As an alternative I'm providing you with a piece of code that will let you use it for ANY self switch of ANY event on the current map.

Code:
class Game_Interpreter
  def self_switch(char, ev_id=0)
    ev_id = @event_id if ev_id == 0
    $game_self_switches[[@map_id, ev_id, char]]
  end

  def set_self_switch(char, value, ev_id=0)
    ev_id = @event_id if ev_id == 0
    $game_self_switches[[@map_id, ev_id, char]] = value
  end
end
To get its current state:

self_switch('C')

To set it to a different state:

set_self_switch('C', false)

Add another parameter at the end, in both cases, to select a different map event.
The advantage is that you no longer need to include thisEvent, thus saving valuable space there. Plus you get the chance to use it for any map event...
Anyway, what he did was creating a global method available from anywhere which in this case was totally unnecessary and it's a bad practice in fact.
 

RedFawkes

Veteran
Veteran
Joined
Jun 21, 2018
Messages
39
Reaction score
16
First Language
BR Portuguese
Primarily Uses
RMVXA
That's also helpful, thanks!
 

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
61
Reaction score
18
First Language
English
Primarily Uses
RMVXA
@kyonides I think you presume too much ;) I had intended that you would paste the thisEvent method into the Interpreter, although I did not say that (sorry Red). It doesn't make sense outside of the context of an interpreter (if you stop and think for a sec), since get_character is an interpreter method so you can't just call it without a qualifier like I did as a global method.

I don't super get the 'saving valuable space' comment; I don't think the string length being fed into the interpreter is a super huge performance concern. And, if you're writing enough script code in the little script blocks to run out of space, you will probably be happier either splitting it up or turning it into a real script in the script files section; the tiny script editor is not a pleasant place to write code.

I do agree that method calls in ruby are slower than I'd like (I've done fairly extensive performance profiling of the VX ACE ruby engine) but at the rate Red is calling this, an extra method call is very, very unlikely to have a perceptible impact on performance. I make probably thirty method calls per monster per frame with thirty to fifty monsters on a map and can still run at 120 FPS on hardware that was modern 4 years ago. So I think in this case readability, simplicity and ease of use would trump flexibility. Whether you like thisEvent.setSelfSwitch('A', true) or having it be implicit in set_self_switch('A', true) is, I think, a matter of taste :) I clearly am not a ruby programmer first (C, C++ and C#) as you can tell from my camel case and lack of underscores, so I think my tastes do differ from the general Ruby community.
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

We are truly intellectuals.
Without using violence without weapons
Our group has as many as 1.3 million people and more than 1 hundred thousand talks.
We gathered in an hour, more than 4.5 Half a hundred thousand



I got a Sword.
I got an Axe.
I got two Swords.
Nothing to see here, just walking with my dog.
I haven’t worked on my game in a while. Tomorrow might be the day.
*squeals* :kaoblush:


I've been experimenting with improved memory management again, which allowed me to include items to increase player path length. These are both 44 grid spaces long, and stable. :LZSexcite:
I FINALLY DID IT! 5 years making my very first game and I just officially released it on STEAM..... OMG..... I can say I finished a game! The feelings!!!!!

Forum statistics

Threads
104,462
Messages
1,006,471
Members
135,971
Latest member
Akasheee
Top