channelling time [SOLVED]

Started by lafouine88, Wed 17/03/2021 20:27:38

Previous topic - Next topic

lafouine88

Hey guys

I'm having trouble with the RPG phase of my game. Basically you can shoot the monsters using the mouse, left click is immediate shooting, right click is a long channelled shot.
And this is where I have difficulties...

So my system is probably veeeery unclean but so far it worked, here goes the system.

Code: ags
function on_mouse_click(MouseButton button)
{ 

     if (button == eMouseLeft)
{
    Room.ProcessClick(mouse.x,mouse.y, eModePickup);
  player.LockView(110);
  player.Animate(7, 1, eOnce, eBlock);        ///////////shooting animation
 player.UnlockView();   
}
else if(button==eMouseRight){
      Room.ProcessClick(mouse.x,mouse.y, eModePickup);
      oCast.Visible=true;
      oCast.SetView(117);
      oCast.Animate(1, 1, eOnce, eNoBlock);     ///////////////a castbar animation that fills itself (wow style)
    player.LockView(15);
  player.Animate(12, 20, eOnce, eNoBlock); //////////////the charged shooting animation    
}  
}


Then on the monster, I put an "anyclick" function :

Code: ags
function oMonster_AnyClick()             
{
if(mouse.IsButtonDown(eMouseLeft)){
  shoot(damageplayer, armorMonster);            ////////////goes to a shooting function that calculates the damage dealt according to armor...named "finaldamage"
 Monsterdamagedisplay(IntToFloat(finaldamage));          ///////////display function of the damage
 
}
else if(mouse.IsButtonDown(eMouseRight)){    ///////////pretty much the same but damage is 3* higher   [RIGHTCLICKLINE]
  shoot(3.0*damageplayer, armorMonster);
 Monsterdamagedisplay(IntToFloat(finaldamage));
}
 
}


That is all is needed I guess. I added a bunch of repetedly execute conditions to remove the castbar and unlock the player's view when he's done channeling. That's no problem and I don't think it's necessary to add the codes here.

So the problem is : How to add the condition to wait for the channel bar to be full before applying the damage?

I thought of adding a bool when the channel bar is full and added the condition (&&bool==true)in [RIGHTCLICKLINE], that would turn off when right clicking, but it does'nt work as planned. It applies the damage before waiting for the bar to be filled.

I don't know if all of this is very clear so I'm not going to add too many details. Maybe one of you already used this system and can help out ? I'm running out of ideas here.

Thanks a lot :)

Matti

I'm not sure if this is sufficient or if you need to alter your system a bit, but you could just check the castbar's frame.

Code: ags
else if(mouse.IsButtonDown(eMouseRight) && oCast.Frame == x){ // x being the last frame of the animation 

lafouine88

Hey Matti, thanks for you help.

I tried your trick but it behaves the same way as when I used a bool that triggered only at the end of cast animation. It seems it checks if the conditions are filled at the moment you click. It does not take into account the button kept down.

Another idea I just tried was this :
Code: ags

int chan=0; ///on top
//.......
else if(mouse.IsButtonDown(eMouseRight)){    ///////////pretty much the same but damage is 3* higher   [RIGHTCLICKLINE]
if(chan==40){                       ///////time checker, supposed to work according to the guide
  shoot(3.0*damageplayer, armorMonster);
 Monsterdamagedisplay(IntToFloat(finaldamage));
chan=0;}
else{chan++;}
}


I was really optimistic about that but it did'nt work out and I don't know why :/

Matti

Can you explain exactly how it's supposed to work?

Do you want the player to hold down the right mouse button until the bar is filled up, and upon releasing the mouse button, damage should be done and an animation should play? And does the player need to hover over the enemy while doing so, or only when starting to hold the button down, or only when releasing the button?

lafouine88

Hi again :)

Ideally, I want the player to click on the target and stay blocked on it( so no hovering out and in, but I think that will be a bit hard, so maybe just when you release the button, in the end).
Exactly as you said, you hold down the right button until the bar fills (I used an animation but it can be a gui too I guess, although I didn't manage to make it work), and the damage deals only when you release the button and the cursor is still on the target. it can be interupted any time but then no damage is dealt.
As for the animation, I thought the animation starts when you hold the bar and finishes naturally when the bar is full..can be interrupted too

Which makes me think...maybe I should simply use a timer. I'll try this :)

Thanks again

arj0n

#5
Why not use a do-while?

In this example oCast's last frame (when bar is full) is frame nr. 10:

Code: ags

if (mouse.IsButtonDown(eMouseRight))
{
  do 
  {
    oCast.Frame = 0; //set oCast to first frame again
    shoot(3.0*damageplayer, armorMonster); //shooting animation
    Monsterdamagedisplay(IntToFloat(finaldamage)); //display function of the damage
  }
  while (oCast.Frame == 10); //when oCast reaches its last frame, so bar is full
}

Crimson Wizard

#6
Quote from: arj0n on Thu 18/03/2021 12:19:42
Why not use a do-while?

Depending on use-case that could be an okay or a bad strategy, as that will lock most of the remaining game script. That's something to consider beforehand.

Regarding the code you posted, tbh I dont understand what it's supposed to be doing. But one thing, I assume oCast.Frame increases by a non-blocking animation? - in this case you might need to put "Wait(1)" in the loop to let engine update itself.
Also "while (oCast.Frame == 10)" means "loop while Frame equals 10", so it will loop as long as Frame is 10 in the end of block, otherwise ends... maybe you meant "oCast.Frame < 10" ? But then, why is shoot being called every loop iteration? This is something I find confusing in this code.

arj0n

#7
 >>I assume oCast.Frame increases by a non-blocking animation?
Yes.

>>Also "while (oCast.Frame == 10)" means "loop while Frame equals 10"
isn't the frame set to 0 when (oCast.Frame == 10) is met?

The purpose of the example was to set frame to frame0, run the shooting animation and the Monsterdamagedisplay while frame reaches frame10 while a specific key is pressed...

Crimson Wizard

#8
Quote from: arj0n on Thu 18/03/2021 13:42:35
The purpose of the example was to set frame to frame0, run the shooting animation and the Monsterdamagedisplay while frame reaches frame10 while a specific key is pressed...

This is what the code does:

It sets Frame = 0 (I don't remember what will happen if animation is running at the same time).
Then it runs 2 actions (I guess they are blocking? judging by above comments)
Then it checks whether Frame is 10 or not, and only loops if it's exactly 10 to start all over again, otherwise quits.

It does not check that the key is still pressed, it checks the key only once at the top.

EDIT:
Basically, what would happen with this code:
- if non-blocking oCast animation will complete to the end (frame 10) while "shoot" and "Monsterdamagedisplay" are passing, then this continues to loop again.
- if non-blocking oCast animation will not end till then, then the loop will finish.

arj0n

#9
My thought was it should be placed in a rep exec. in order to check that the key is still pressed.
But I'm sure you'll have a better solution  :)

EDIT:
Wait, I did assume that the 3 actions are fired only once when frame == 10, because the frame than is set to frame 0 and the loop breaks.

Crimson Wizard

#10
Quote from: arj0n on Thu 18/03/2021 14:07:39
My thought was it should be placed in a rep exec. in order to check that the key is still pressed.

The thing is, that the conditions and use of loop are strange.

If it's put in rep exec, then what happens is that whole code will be run from the start again and again, and shooting animation will start over every single game frame while right button is pressed.
No, this is not what will happen.... because it will run a blocking shoot animation.

The point is, it will not be checking the pressed key all the time while shoot animation is running, because it's only checked at the beginning, and never during shoot animation itself. This is regardless of where the code is put.

Matti

I would use the function where the enemy is clicked to start a timer variable and to lock the cursor on the enemy. Then, in the rep-exec I would check whether the button is still pressed or not. If the bar is full (e.g. the timer reached a certain value) damage is done and the cursor is unlocked. For the bar I would use a GUI button that increases in size according to the value of the counter, instead of using an animation.

Maybe later today I can test this and post a proper code.

arj0n

That sounds like a better way to handle this indeed.

Crimson Wizard

#13
Quote from: arj0n on Thu 18/03/2021 14:07:39
Wait, I did assume that the 3 actions are fired only once when frame == 10, because the frame than is set to frame 0 and the loop breaks.

No of course not, this is not how the above loop works. These actions run as soon as each loop iteration begins.

Loop is not broken in any random place too. The condition is tested only in the end of each iteration (unless you explicitly test it yourself and break in the middle).

This script is executed command by command in their literal order:

- Set frame to 0
- Run 2 actions
- Check if frame is 10 or not. If it's 10 then repeat.

arj0n

Oops, "checks expression AFTER executing statements, not before".
Sorry...  :(

Matti

#15
Okay, so I tried out what I wrote above. I created a GUI for the Castbar and made it 80px wide. I created two global ints: Char_Lock keeps track of which character the mouse should hover over and Charge_Counter which determines how much the castbar has progressed.

EDIT: I just realized that your monster is an object, not a character. I think it should work the same way though, if you change character to object in that code.

This is the AnyClick function of the monster:

Code: ags
function cMonster_AnyClick()
{
  if (Mouse.IsButtonDown(eMouseRight) && Charge_Counter == 0)
  {
     Char_Lock = x;                         // x being the ID of the character
     Charge_Counter = 0;
     gChargeBarGUI.Visible = true;
  }
}


Then put this in the room's rep exec:

Code: ags
function room_RepExec()
{
  if (gChargeBarGUI.Visible)
  {
    if (Mouse.IsButtonDown(eMouseRight))
    {
      if (Charge_Counter < 80) Charge_Counter ++; // increase the castbar/counter. In this example, it increases 1 px per frame, so it's fully charged after two seconds (with the standard gamespeed of 40 frames per second).
      gChargeBarGUI.Width = Charge_Counter;
      // Edit: Forgot this part:
      if (Charge_Counter == 80)
      {
        // do stuff, e.g. shooting animation, damage calculation
        gChargeBarGUI.Visible = false;
        Char_Lock = 0;
      }
    }
    else 
    {
      Charge_Counter = 0;
      gChargeBarGUI.Visible = false;
    }
  }
}


And this has to go in the late_repeatedly_execute_always():

Code: ags
function late_repeatedly_execute_always()
{
  if (Char_Lock > 0)
  {
    if (Mouse.IsButtonDown(eMouseRight))
    {
      Mouse.SetPosition(character[Char_Lock].x + 2,  character[Char_Lock].y - 10); // locks the cursor position to the enemy character, the exact position may need to be adjusted
    }
    else Char_Lock = 0;
  }
} 


I think this works as intended, but could be improved. For example right now the cursor snaps to a certain position and you might want the cursor to stay where it is when right-clicking the monster.

Also I first wanted to increase the width of a button on the GUI but for some reason it didn't work. Increasing the width of the whole GUI did work, so I deleted the button. You might want to have some border or other graphics around the bar though.

lafouine88

#16
Hi guys

Thanks a lot for your answers. I didn't know about the "do/while" thing. I've never used "do" actually^^ So thanks arjon and crimson for the debate :)

Matti I tried your code and that is what I wanted and tried to do but wasn't able to code properly :s.
It works very fine(juste needs to add "Charge_Counter=0; after line 14 of repetedly execute, and gChargeBarGUI.Width=1; after line 6 of "anyclick"). The cursor doesn't stay locked on the enemy and I don't really know how "late repetedly execute always" works. But after thinking about it I guess I'll leave the possibility of moving the mouse and charging "anywhere" but the damage will be dealt only if the cursor is on the monster when the bar is finished casting. If this is harder than I thought, maybe I'll come back "^^

Thanks again to everybody :)

EDIT : Locking the cursor does work, it just didn't because my monster's ID was 0, I guess because on other monsters it's ok.

lafouine88

#17
Actually after trying a bit, this locked-on system is pretty cool. So thanks a million for your help  :-D
One last thing, I changed the line 7 of late repeatedly execute :
Code: ags

Mouse.SetPosition(mouse.X,  mouse.Y); // locks the cursor position 


That way it just blocked the mouse cursor where it was.

->SOLVED

Matti

Cool, I'm glad I've been helpul  :)

As for the lock-on: I assumed the monster(s) would move, that's why I used the character's coordinates. But if they don't your code for the mouse position is of course quite fitting  ;)

SMF spam blocked by CleanTalk