Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Khris

#381
I was working on a math based solution but it got more complicated quickly. I'll try your original approach again, give me a few hours :)

Edit: got it:

Code: ags
// room script file

enum KiteState {
  eKiteCircling, eKiteSwoopingIn, eKiteSwoopingOut
};

KiteState kite_state = eKiteCircling;
int exposed_timer;

int circling_timer;
int kite_region; // 0 = right, 1 = top right, etc.

function room_Load()
{
  cKite.z = -60;
  kite_region = 0;
}

void caught_by_kite() {
  Display("Caught by kite");
  // QuitGame(0);
}

bool kite_is_outside_screen() {
  ViewFrame* vf = Game.GetViewFrame(cKite.NormalView, cKite.Loop, cKite.Frame);
  int hw = Game.SpriteWidth[vf.Graphic] / 2;
  int h = Game.SpriteHeight[vf.Graphic];
  if (cKite.x < -hw || cKite.x > Game.Camera.Width + hw) return true;
  return cKite.y < 0 || cKite.y > Game.Camera.Height + h;
}

function room_RepExec() {

  // is player in danger
  bool danger_zone = Region.GetAtRoomXY(player.x, player.y) == region[5];
  
  if (danger_zone && cEgo.IsCollidingWithChar(cKite)) caught_by_kite(); //death script

  oDanger.Visible = danger_zone; // obj currently being used for testing
  exposed_timer = (exposed_timer + 1) * danger_zone; // increase / reset timer

  if (kite_state == eKiteCircling) {
       
    // kite swoops for player, continuing straight if player moves
    if (exposed_timer > 100) { 
      exposed_timer = 0;
      kite_state = eKiteSwoopingIn;
      float dx = IntToFloat(player.x - cKite.x);
      float dy = IntToFloat(player.y - cKite.y);
      float f = 2000.0 / Maths.Sqrt(dx * dx + dy * dy);
      cKite.Walk(cKite.x + FloatToInt(f * dx), cKite.y + FloatToInt(f * dy), eNoBlock, eAnywhere);
    }    
    else {
      circling_timer++;
      if (circling_timer >= 50 && !cKite.Moving) {
        kite_region = (kite_region + 1) % 8;
        float a = (IntToFloat(-kite_region)) * (Maths.Pi / 4.0);
        cKite.x = Game.Camera.Width / 2 + FloatToInt(IntToFloat(Game.Camera.Width) * Maths.Cos(a), eRoundNearest); 
        cKite.y = Game.Camera.Height / 2 + FloatToInt(IntToFloat(Game.Camera.Height) * Maths.Sin(a), eRoundNearest) - cKite.z; 
        circling_timer = 0;
        // visible top left
        if (kite_region == 3 && !cKite.Moving) {
          cKite.x = 300;
          cKite.y = -100 - cKite.z;
          cKite.Walk(-300, 200 - cKite.z, eNoBlock, eAnywhere);
        } // visible bottom right
        if (kite_region == 7 && !cKite.Moving) {
          cKite.x = Game.Camera.Width - 300;
          cKite.y = Game.Camera.Height + 100 - cKite.z;
          cKite.Walk(Game.Camera.Width + 300, Game.Camera.Height - 200 - cKite.z, eNoBlock, eAnywhere);
        }
      }
    }
  }
  
  // this makes the kite stop as soon as it has left the screen
  if (kite_state == eKiteSwoopingIn && !kite_is_outside_screen()) kite_state = eKiteSwoopingOut;
  if (kite_state == eKiteSwoopingOut && kite_is_outside_screen()) {
    kite_state = eKiteCircling;
    float a = Maths.ArcTan2(IntToFloat(cKite.y - Game.Camera.Height / 2), IntToFloat(cKite.x - Game.Camera.Width / 2));
    kite_region = (FloatToInt(-a * 4.0 / Maths.Pi, eRoundNearest) + 8) % 8;
  }
}
#382
AGS Engine & Editor Releases / Re: AGS 3.6.1
Fri 05/04/2024 15:37:46
Just had a 2nd weird accident:

Code: ags
    ds.DrawCircle(x ü this.Id, y, 2);

(The ü key is right next to the + key.) The above line caused the compiler to hang; it showed the box saying "Please wait while your scripts are compiled" but then nothing happened. The editor didn't really freeze either, the spinner was still spinning. It just never
 got to the next step. I had to force-close the editor.
#383
AGS Engine & Editor Releases / Re: AGS 3.6.1
Fri 05/04/2024 15:01:39
n is a script level array of structs:

Code: ags
// header

struct Node {
  int id;
  float x, y;
  int polyId;
  int prevId;
  int nextId;
  import Vec2D* Get();
  import Vec2D* GetPrevVec();
  import Vec2D* GetNextVec();
  import float GetAngle();
};

// main

#define MAX_NODES 1000

Node n[MAX_NODES];


bool Poly::IsInside(float x, float y) {
  int nid = this.firstId;
  Vec2D* min = n[nid].Get(), max = n[nid].Get(), node, s1, s2, d2;
  for (int i = 1; i < this.nodeCount; i++) {
    nid = n[nid].nextId; // move to next node
    node = n[nid].Get();
    if (node.x < min.x) min.x = node.x;
    if (node.y < min.y) min.y = node.y;
    if (node.x > max.x) max.x = node.x;
    if (node.y > max.y) max.y = node.y;
  }
  if (x < min.x || x > max.x) return false;
  if (x < min.y || y > max.y) return false;
  s1 = new Vec2D;
  s1.y = (min.y + max.y) / 2.0;
  s1.x = 2.0 * min.x - max.x;
  Vec2D* d1 = new Vec2D;
  d1.x = x - s1.x;
  d1.y = y - s1.y;
  int cuts = 0;
  nid = this.firstId;
  for (int i = 0; i < this.nodeCount; i++) {
    s2 = n[nid].Get();
    d2 = n[nide].GetNextVec();                  ////////// <----------- error here leads to oob in GetNextVec function
    IntersectResult* ir = Intersect(s1, d1, s2, d2);
    if (ir.yes) cuts++;
    nid = n[nid].nextId;
  }
  return cuts % 2 == 1;
}

I just realized: the line itself "works", but whatever n[nide] references, when its GetNextVec method is called, this.nextId is now a huge number and causes the oob error:

Code: ags
Vec2D* Node::GetNextVec() {
  if (this.nextId == -1) return null;
  Vec2D* r = new Vec2D;
  r.x = n[this.nextId].x - this.x;   /////// <------ this causes the oob error
  r.y = n[this.nextId].y - this.y;
  return r;
}

So apparently "nide" is inside 0...999 but n[nide].nextId is something like 104674 or 65833, i.e. random bytes I assume.

Edit: I tried
Code: ags
    d2 = n[nide].GetNextVec();
    Display("nide: %d", nide);
but the Display line correctly causes a compile time "undefined symbol" error.
#384
Assuming this is just about clicking the character, you can use the FollowCharacter command:

Code: ags
 // in game_start or room_Load
  cPanel.FollowCharacter(cRobot, FOLLOW_EXACTLY, 0);

cPanel is a second character with walkcycle sprites based on the robots'. The idea is that the panel is not always visible; i.e. the front walkcycle will contain fully transparent sprites, while the back cycle sprites contain just the panel, but have the same dimensions as the robot's so it's always in the right place.

The special FollowCharacter parameters causes the panel character to always be in the exact same position as the robot, so this should work out in theory.

Now all you need is a click handler for the panel character.
#385
AGS Engine & Editor Releases / Re: AGS 3.6.1
Fri 05/04/2024 08:30:57
I just found another issue. This compiles fine:
Code: ags
  d2 = n[nide].GetNextVec();

However, there is no variable called "nide"; this was a typo and supposed to be "nid". The compiler doesn't catch this at all, and during the game the non-existent variable's value will be a random integer (I noticed this because I got an out of bounds error for the array).
I tested this to make sure and it also compiles just fine:
Code: ags
  d2 = n[nidfdlghkjshdf].GetNextVec();

The error already was in build 3.6.1.19 so I assume it has been there for a long time.
#386
Here's a pastebin: https://pastebin.com/GcVVzD1U
(wetransfer links only last a week)
#387
The most basic way to implement hotspot labels is to use a GUI with a label that has @OVERHOTSPOT@ as its text.
Which template did you use to make your game?
#388
The second variant will take even longer.
Do this instead:
Code: ags
  gui1.x = (globalint1 >= 10) * 10;

You cannot code a "global int has changed its value" event without checking the variable every frame, so there's no alternative to rep_exe here.

Edit: this was less meant to increase performance and more to increase the readability of the main rep_exe function.
#389
Afaik the GUI's visibility is what determines whether the game pauses; you have to switch it to normal and implement the PopUp behavior yourself.

Like this:
Code: ags
  // inside repeatedly_execute:
  if (!gTopbar.Visible && mouse.y < 10) gTopbar.Visible = true;
  else if (gTopbar.Visible && mouse.y >= gTopbar.Height) gTopbar.Visible = false;

This should also work:
Code: ags
  // inside repeatedly_execute:
  GUI* g = gTopbar; // set GUI
  g.Visible = mouse.y < 10 || g.Visible && mouse.y < g.Height;
#390
Quote from: Akril15 on Thu 28/03/2024 11:36:24but I'm starting to think that having the kite completely offscreen except when it's attacking might be the simplest solution here.

I assumed that's already the case though? My code is based on that very idea: the kite is never visible until it attacks, then it leaves the screen again. Is that not what's happening for you?

Also I'm still not entirely sure what the desired pattern actually is. How is it breaking?
It's not even that hard to implement a more complex behavior, but I'm still fumbling in the dark here.
#391
That is amazing! Watching the column rise while the timer ticks down is nerve-wracking, nice job!

Spoiler
I noticed that when you have unlocked a bunch of levels, then replay one of the first ones and beat it again, the game still always unlocks the next locked level. So it looks like you can just beat level 1 over and over again to unlock every level?
[close]
#392
@eri0o

It takes 13 seconds to boot to the console selection screen (including holding down the button to power it on). Way, way faster than for instance RetroPie.
You cannot connect this one to the internet; you simply put your games on an SD card and pop it in the 2nd SD card slot. Upgrading is possible if you flash a suitable image on the card in the 1st slot, but there's no reason to do this once you have a working system; it's not like you need to constantly update a gameboy emulator core :)

I've looked into DIY kits but I primarily wanted a new, reliable, fast handheld that emulates all the 80's and 90's consoles. The kits all seem to be Raspberry based and therefore running Retropie, so it'll take you about 2 minutes from powering it on to being able to play, which is a dealbreaker for me. I also find the controller configuration to be a massive headache. (And let's not talk about the fact that a Raspberry doesn't have a power switch...)

The RG35XX also has the Gameboy Pocket form factor but features the full set of PS1 controller buttons and a great screen. It also does use RetroArch under the hood, so you have the full range of settings to tinker with, if you want to.

I'm pretty sure a homebrew GB game will run fine on most GB cores, but I get the appeal of putting your own cartridge into an actual Gameboy ;-D
#393
Just to be clear: bird, enemy and cKite are the same character? As in, the bottom_to_right function is supposed to reposition cKite?

Also, what exactly is cKite supposed to do after a failed attack? Visibly move to a specific spot (or spots, depending on its position)?

What exactly was this line from your original code supposed to do?
Code: ags
  if (bird_r==false) bottom_to_right(); //moves enemy from (810, 900) to (1187, 207)

If I understand you correctly:
If after a failed attack the kite ends up off-screen at the top, their next attack is supposed to come from the left?
And if they ended up on the bottom, from the right?

If so, all you need to do is (this replaces the existing block):
Code: ags
  if (kite_state == eKiteSwoopingOut && kite_is_outside_screen()) {
    cKite.StopMoving();
    if (cKite.y < cKite.z) {
      cKite.x = -200;
      cKite.y = 300;
    }
    else if (cKite.y > Game.Camera.Height) {
      cKite.x = 1250;
      cKite.y = 300;
    }
    kite_state = eKiteCircling;
  }
#394
I got this a few months ago:
https://anbernic.com/products/rg35xx?variant=43649446805761
(from here: https://www.geekbuying.com/item/ANBERNIC-RG35XX-Handheld-Game-Console-64GB-Card-Grey-519449.html)

It boots really fast, has a great UI and emulates everything up to and including Playstation 1 games.
It costs about as much as the Zelda G&W but also plays the SNES, GBC and GBA games :-D
You can even use it like a console if you connect it to a TV and a gamepad.
#395
Sorry, I didn't mean for you to remove these crucial lines. What I meant is you can add lines in there to run additional code.
#396
If you look at lines 60 and 61 in the second to last snippet, that's what runs once each time the kite has left the screen. You can reposition it there instead.
I'm not sure how exactly this is supposed to work though; you said "comes at the player from the lower right and exits the screen at the upper right" but I guess you meant "upper left"?

If it left the screen at the upper left, from where should it enter the screen the next time it attacks?
#397
Yeah, I was debating whether to address this but didn't.

The thing is, you're already calculating the distance between the enemy and the player anyway. So you can use that instead to determine whether the player got caught.
Maybe the position of their respective feet isn't the best choice for the collision check?

However if that's the case, shifting the sprite down using like cKite.z = -200; might do the trick, although this requires adjusting the y coordinates in my off-screen check.

Code: ags
bool kite_is_outside_screen() {
  ViewFrame* vf = Game.GetViewFrame(cKite.NormalView, cKite.Loop, cKite.Frame);
  int hw = Game.SpriteWidth[vf.Graphic] / 2;
  int h = Game.SpriteHeight[vf.Graphic];
  if (cKite.x < -hw || cKite.x > Game.Camera.Width + hw) return true;
  return cKite.y < cKite.z || cKite.y > Game.Camera.Height + h + cKite.z; // fixed to include z coordinate
}
#398
It's probably because I'm always logged in; if you keep clearing your cookies and never log in, youtube can't really tell you're the same person.

Which means these filters are primarily for people who want to stay logged in, I guess.
#399
There's another typo in there :P
They y coordinate of the walk command is supposed to use kite_dy instead of kite_dx.

Another issue is that if the kite moves too far outside the screen, it won't move far enough next time.
I already made a test game to troubleshoot this, so give me a few minutes to implement this properly.

Edit:
Here's my script, seems to work fine:

Code: ags
// room script file

enum KiteState {
  eKiteCircling, eKiteSwoopingIn, eKiteSwoopingOut
};

KiteState kite_state = eKiteCircling;
int exposed_timer;
int kite_circling_timer;

bool bird_l, bird_r;

void caught_by_kite() {
  Display("Caught by kite");
  QuitGame(0);
}

void bottom_to_right() {
}

void top_to_left() {
}

bool kite_is_outside_screen() {
  ViewFrame* vf = Game.GetViewFrame(cKite.NormalView, cKite.Loop, cKite.Frame);
  int hw = Game.SpriteWidth[vf.Graphic] / 2;
  int h = Game.SpriteHeight[vf.Graphic];
  if (cKite.x < -hw || cKite.x > Game.Camera.Width + hw) return true;
  return cKite.y < 0 || cKite.y > Game.Camera.Height + h;
}

function room_RepExec() {

  // is player in danger
  bool danger_zone = Region.GetAtRoomXY(player.x, player.y) == region[5];
  
  if (danger_zone && cEgo.IsCollidingWithChar(cKite)) caught_by_kite(); //death script

  oDanger.Visible = danger_zone; // obj currently being used for testing
  exposed_timer = (exposed_timer + 1) * danger_zone; // increase / reset timer

  if (exposed_timer > 100) {
    exposed_timer = 0;
    
    // kite swoops for player, continuing straight if player moves
    if (kite_state == eKiteCircling) { 
      float kite_dx = IntToFloat(player.x - cKite.x); // x distance from enemy to player
      float kite_dy = IntToFloat(player.y - cKite.y); // y distance from enemy to player
      float f = 5000.0 / Maths.Sqrt(kite_dx * kite_dx + kite_dy * kite_dy); // factor to extend vector to a length of 1500
      int targetx = cKite.x + FloatToInt(kite_dx * f);
      int targety = cKite.y + FloatToInt(kite_dy * f);
      cKite.Walk(targetx, targety, eNoBlock, eAnywhere);
      kite_state = eKiteSwoopingIn;
    }
  }
  
  // this makes the kite stop as soon as it has left the screen
  if (kite_state == eKiteSwoopingIn && !kite_is_outside_screen()) kite_state = eKiteSwoopingOut;
  if (kite_state == eKiteSwoopingOut && kite_is_outside_screen()) {
    cKite.StopMoving();
    kite_state = eKiteCircling;
  }

  if (kite_state == eKiteCircling && !danger_zone) { //"patrolling" script - happens while char is in safe zone

    kite_circling_timer++;

    if (kite_circling_timer == 200) {

      if (bird_r == false) bottom_to_right(); //moves enemy from (810, 900) to (1187, 207)
      else if (bird_l == false) top_to_left(); //(161, 100) to (-130, 381) 
      kite_circling_timer = 0;

    }

  } //circling=true

}
#400
Back a few months ago they suddenly showed a popup that said something like "you have three videos left to watch while adblock is turned on".
I'm always logged in, so that might have contributed to this.

Anyway, the goddamn ads are gone for now. They are incredibly annoying, and it was some of the worst ones out there. Part of it was the same boring and stupid ads for detergents or shampoo I already loathe from network tv, the rest was Hero Wars and a bunch of crypto and health scams. (I even reported one ad as being a ponzi scheme and they took it down (laugh)).
SMF spam blocked by CleanTalk