Register FAQ Search Today's Posts Mark Forums Read
Go Back   JazzJackrabbit Community Forums » Open Forums » JCS & Scripting

AngelScript Requests & Help

Reply
 
Thread Tools
PurpleJazz PurpleJazz's Avatar

JCF Member

Joined: Aug 2006

Posts: 823

PurpleJazz is OFF DA CHARTPurpleJazz is OFF DA CHARTPurpleJazz is OFF DA CHART

Jan 6, 2018, 08:23 AM
PurpleJazz is offline
Reply With Quote
Quote:
Originally Posted by AvalancheMaster View Post
Question time!

I want to combine the Slide event in my level with Steady Light. I'm using Mez03, and I want the neon-tube-like animated platforms to light up in the dark. So far I have no idea how to accomplish that.

Also, while on the same topic, is there a way to change the "Illuminate Surroundings" parameter to act as a steady light, rather than Flickering?
I assume this is the only time you're using the Slide event? If that's the case, try the following script:

Code:
void onLevelBegin() {
    for (int x = 0; x < jjLayerWidth[4]; x++) {
        for (int y = 0; y < jjLayerHeight[4]; y++) {
            if (jjEventGet(x,y) == AREA::SLIDE) {
                jjOBJ@ o = jjObjects[jjAddObject(OBJECT::STEADYLIGHT, (x*32)+16, (y*32)+16)]; //lights go in the middle of tiles
                o.light = 20;
                o.lightType = 3;
            }
        }
    }
}
Again, assuming there's consistency in the objects you're using illuminate surroundings on, you can set their light parameter to 20 and lightType to 3 to achieve the same result as above.
AvalancheMaster AvalancheMaster's Avatar

JCF Member

Joined: Sep 2013

Posts: 89

AvalancheMaster is doing well so far

Jan 6, 2018, 09:12 AM
AvalancheMaster is offline
Reply With Quote
That doesn't seem to do the job. Also, doesn't lightType use names?
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Jan 6, 2018, 09:33 AM
Violet CLM is offline
Reply With Quote
I'm guessing yours is a single player level and PJ is writing code for a multiplayer level (or at least testing it in a single screen). Try o.deactivates = false;?
__________________
AvalancheMaster AvalancheMaster's Avatar

JCF Member

Joined: Sep 2013

Posts: 89

AvalancheMaster is doing well so far

Jan 6, 2018, 09:41 AM
AvalancheMaster is offline
Reply With Quote
Single Player indeed. I included that line, but it still doesn't seem to work.
PurpleJazz PurpleJazz's Avatar

JCF Member

Joined: Aug 2006

Posts: 823

PurpleJazz is OFF DA CHARTPurpleJazz is OFF DA CHARTPurpleJazz is OFF DA CHART

Jan 6, 2018, 10:38 AM
PurpleJazz is offline
Reply With Quote
Quote:
Originally Posted by AvalancheMaster View Post
Single Player indeed. I included that line, but it still doesn't seem to work.
My bad; I overlooked the fact that your code was probably intended for SP and forgot to accommodate it accordingly.

The following script has been tested and works perfectly for me, so if this still doesn't work it means that there may be an error elsewhere in your script.

Code:
void onLevelBegin() {
    for (int x = 0; x < jjLayerWidth[4]; x++) {
        for (int y = 0; y < jjLayerHeight[4]; y++) {
            if (jjEventGet(x,y) == AREA::SLIDE) {
                jjOBJ@ o = jjObjects[jjAddObject(OBJECT::STEADYLIGHT, (x*32)+16, (y*32)+16)]; //add in middle of tiles
                o.light = 20;
                o.lightType = 3;
                o.deactivates = false;
            }
        }
    }
}

void onLevelReload() {
    onLevelBegin(); //add lights again after death
}
AvalancheMaster AvalancheMaster's Avatar

JCF Member

Joined: Sep 2013

Posts: 89

AvalancheMaster is doing well so far

Jan 7, 2018, 12:46 PM
AvalancheMaster is offline
Reply With Quote
Thanks, that worked!

I'm also trying to make those areas one-way, as well as slide. Is there any way to do this?

So far any attempt to achieve this has resulted either in Slide-only, or One-way-only tiles, depending on the ordering of the code.
Code:
void onLevelBegin() {
    for (int x = 0; x < jjLayerWidth[4]; x++) {
        for (int y = 0; y < jjLayerHeight[4]; y++) {
            if (jjEventGet(x,y) == AREA::SLIDE) {
                jjOBJ@ xenonlight = jjObjects[jjAddObject(OBJECT::STEADYLIGHT, (x*32)+16, (y*32)+16)]; //add in middle of tiles
                xenonlight.light = 20;
                xenonlight.lightType = 3;
                xenonlight.deactivates = false;
                jjEventSet(x, y, AREA::ONEWAY);
                jjEventSet(x, y, AREA::SLIDE);
            }
        }
    }
Also, I am wondering if there is a way to force an animation on a specific frame, depending on player's location. Because my idea is somewhat complicated, let me try to provide an example:

If you have a 10-frame animation, with frames a[1], a[2], a[3], a[4], a[5] ... a[9], a[10], instead of using speed to control the animation, you are using player's position. As the player moves in a positive direction (x and y get bigger), the frames move in chronological order. As the player moves in a negative direction (x and y get smaller), the animation runs in reverse.

When the player stops moving, the animation also stops.

This should apply to all layers, so an animation is the only feasible way I can imagine implementing this.

I want to try to implement faux reflections in a tileset, that's the whole idea behind my question.
happygreenfrog happygreenfrog's Avatar

JCF Member

Joined: Jan 1970

Posts: 23

happygreenfrog has disabled reputation

Jan 22, 2018, 07:11 AM
happygreenfrog is offline
Reply With Quote
So, I'm trying to code a custom boss that summons a bunch of enemies, including summoning low HP bosses as enemies, but I've run into an issue: I'm having a hard time making things immune to (or at least not 1HKO'd by) Spaz's kick and Jazz's uppercut. Setting things to use playerHandling mode HANDLING::SPECIAL "works"... but also removes non-bullet collision detection for most enemies and the main boss (the latter would actually be a good thing if it was consistent, since I don't want it to be hurt by anything other than time passing anyways, but it's too inconsistent to be useful). It also has an issue where setting the bosses to have that collision mode means they beat the level when killed, when you're only supposed to win the level by letting the main boss run out of HP. Basically, it's supposed to be a survival challenge.

Here's the full script for the level as of now, including an attempt at a work-around for the issue where killing bosses clears the level (the work-around works, but only about half the time for some reason):
Code:
#include "MLLE-Include-1.4.asc"
const bool MLLESetupSuccessful = MLLE::Setup();
#pragma require "Tube.j2t"
#pragma require "Medivo.j2t"
#pragma require "hgfCow-MLLE-Data-1.j2l"
#pragma require "Carrot1.j2t"
#pragma require "hgfCow.j2l"

//a special thanks to Sir Ementaler and Violet CLM for their assistance with writing the script!

void onLevelLoad() {
 jjObjectPresets[OBJECT::DEVILDEVAN].behavior = cowBoss;
 jjObjectPresets[OBJECT::DEVILDEVAN].isFreezable = false;
 jjObjectPresets[OBJECT::DEVILDEVAN].playerHandling = HANDLING::SPECIAL;
 jjObjectPresets[OBJECT::DEVILDEVAN].scriptedCollisions = true;
 jjObjectPresets[OBJECT::DEVILDEVAN].energy = 50;
 jjObjectPresets[OBJECT::DEVILDEVAN].bulletHandling = HANDLING::DESTROYBULLET;
 
 jjObjectPresets[OBJECT::BILSY].bulletHandling = HANDLING::HURTBYBULLET;
 jjObjectPresets[OBJECT::BILSY].playerHandling = HANDLING::ENEMY;
 jjObjectPresets[OBJECT::BILSY].energy = 14;
 jjObjectPresets[OBJECT::BUBBA].bulletHandling = HANDLING::HURTBYBULLET;
 jjObjectPresets[OBJECT::BUBBA].playerHandling = HANDLING::ENEMY;
 jjObjectPresets[OBJECT::BUBBA].energy = 5+(jjDifficulty*4);
 jjObjectPresets[OBJECT::TUFBOSS].bulletHandling = HANDLING::HURTBYBULLET;
 jjObjectPresets[OBJECT::TUFBOSS].playerHandling = HANDLING::ENEMY;
 jjObjectPresets[OBJECT::TUFBOSS].energy = 7+(jjDifficulty*4);
 jjObjectPresets[OBJECT::BOLLY].bulletHandling = HANDLING::HURTBYBULLET;
 jjObjectPresets[OBJECT::BOLLY].playerHandling = HANDLING::SPECIAL;
 jjObjectPresets[OBJECT::BOLLY].energy = 10;
 jjObjectPresets[OBJECT::UTERUS].bulletHandling = HANDLING::HURTBYBULLET;
 jjObjectPresets[OBJECT::UTERUS].playerHandling = HANDLING::ENEMY;
 jjObjectPresets[OBJECT::UTERUS].energy = 10;
 jjObjectPresets[OBJECT::ROBOT].bulletHandling = HANDLING::HURTBYBULLET;
 jjObjectPresets[OBJECT::ROBOT].playerHandling = HANDLING::ENEMY;
 jjObjectPresets[OBJECT::ROBOT].energy = 30;
 
 //just a thing I borrowed from plusPixelMapEx
 jjANIMATION@ anim = jjAnimations[jjObjectPresets[OBJECT::DEVILDEVAN].curAnim];
 anim.frameCount = 1;
 jjANIMFRAME@ frame = jjAnimFrames[anim.firstFrame];
 jjPIXELMAP kingCow(459*32, 3*32, 6*32, 7*32, 4);
 kingCow.save(frame);
 frame.hotSpotX = -frame.width / 2;
 frame.hotSpotY = -frame.height / 2;
}

void onLevelBegin(){
//make sure the main song reloads after encountering the boss
jjMusicLoad("Meatball Parade.ogg");
}

//int rPart(float x,float y) { // render a part of Devan Force
// jjDrawSprite(x, y, ANIM::BIGROCK, 0, 0, 0);
 //void jjDrawSprite(x, y, uint8 (ANIM::BIGROCK), uint8 (0), uint8 (0), int direction = 0, SPRITE::Mode mode = SPRITE::NORMAL, int param = 0, int8 layerZ = 4, uint8 layerXY = 4, int8 playerID = -1)
// return 0;
//}

void cowBoss(jjOBJ@ obj) {
 switch (obj.state) {
  case STATE::START:
   //don't start boss until player has reached boss activation point
   obj.state = STATE::DELAYEDSTART;
   case STATE::DELAYEDSTART:
    //loop players
    for (int i = 0; i < jjLocalPlayerCount; ++i) {
     jjPLAYER@ localPlayer = jjLocalPlayers[i];
     if (localPlayer.bossActivated) {
      localPlayer.boss = obj.objectID;
      obj.state = STATE::START;
     }
    }
    if (obj.state == STATE::START) {
	 //set object to be handled in a more normal way, since we're using Devil Devan as the "base"
     obj.playerHandling = HANDLING::SPECIAL;
	 //load song
	 jjMusicLoad("dang.j2b");
	 //start boss
	 obj.state = STATE::ATTACK;
	 //initialize some important variables
	 obj.age = 60; //boss attack cooldown
	 obj.special = 0; //next attack the boss is going to use
	 obj.var[0] = 0; //move direction
	 obj.var[1] = 0; //reticle height
	 obj.var[2] = 10; //carrot delay
     break;
    }
    return;
  case STATE::KILL:
   //start next stage
   jjNxt(true, false);
  case STATE::DEACTIVATE:
   //reset boss
   jjMusicLoad("Meatball Parade.ogg");
   obj.deactivate();
   break;
  case STATE::ATTACK:
   //draw boss
   jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL, 24);
   //set cow as boss
   for (int i = 0; i < jjLocalPlayerCount; ++i) {
     jjPLAYER@ localPlayer = jjLocalPlayers[i];
     if (localPlayer.bossActivated) {
      localPlayer.boss = obj.objectID;
     }
	}
	
	//just some old code I didn't completely remove
	//for (int i = 1; i < jjObjectCount; i++){
	// jjOBJ@ o = jjObjects[i];
	// if (o.eventID == OBJECT::BOLLY){obj.var[0] == 1;}
	// if (o.eventID == OBJECT::BUBBA){obj.var[0] == 1;}
	// if (o.eventID == OBJECT::BILSY){obj.var[0] == 1;}
	// if (o.eventID == OBJECT::TUFBOSS){obj.var[0] == 1;}
	//}
	//if (obj.var[0] == 0){}
	
	//constantly cool down attack
	obj.age -= 1;
	//prepare summon
	if (obj.age <= 0) {
	 //just some more old code I didn't completely remove
	 //if (obj.special == 1){jjAddObject(OBJECT::TUFBOSS, obj.xPos, obj.yPos+96);}
	 //if (obj.special == 2){jjAddObject(OBJECT::BUBBA, obj.xPos, obj.yPos);}
	 //if (obj.special == 2){jjAddObject(OBJECT::BOLLY, obj.xPos, obj.yPos);}
	 //if (obj.special == 2){jjAddObject(OBJECT::UTERUS, obj.xPos, obj.yPos);}
	 //if (obj.special == 3){jjAddObject(OBJECT::BILSY, obj.xPos, obj.yPos);}
	 
	 //summon an enemy based on next attack chosen
	 if (obj.special == 0){jjObjects[jjAddObject(OBJECT::LIZARD, obj.xPos, obj.yPos)].playerHandling = HANDLING::SPECIAL;}
	 if (obj.special == 1){jjObjects[jjAddObject(OBJECT::BAT, obj.xPos, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;}
	 if (obj.special == 2){jjObjects[jjAddObject(OBJECT::RAVEN, obj.xPos, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;}
	 if (obj.special == 3){jjObjects[jjAddObject(OBJECT::FENCER, obj.xPos, obj.yPos)].playerHandling = HANDLING::SPECIAL;}
	 if (obj.special == 4){jjObjects[jjAddObject(OBJECT::BEE, obj.xPos, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;}
	 if (obj.special == 5){jjObjects[jjAddObject(OBJECT::TUFBOSS, obj.xPos, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;}
	 //if (obj.special == 6){jjObjects[jjAddObject(OBJECT::BEE, obj.xPos, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;
	 // if(jjDifficulty >= 2){jjObjects[jjAddObject(OBJECT::BEE, obj.xPos+10, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;
	 // jjObjects[jjAddObject(OBJECT::BEE, obj.xPos-10, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;}}
	 if (obj.special == 6){jjObjects[jjAddObject(OBJECT::BEE, obj.xPos, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;
	  if(jjDifficulty >= 2){jjObjects[jjAddObject(OBJECT::BEE, obj.xPos+10, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;}}
	 if (obj.special == 7){jjObjects[jjAddObject(OBJECT::BUBBA, obj.xPos, obj.yPos)].playerHandling = HANDLING::SPECIAL;}
	 if (obj.special == 8){jjObjects[jjAddObject(OBJECT::LIZARD, obj.xPos, obj.yPos)].playerHandling = HANDLING::SPECIAL;
	  if(jjDifficulty >= 2){jjObjects[jjAddObject(OBJECT::LIZARD, obj.xPos+10, obj.yPos)].playerHandling = HANDLING::SPECIAL;
	  jjObjects[jjAddObject(OBJECT::LIZARD, obj.xPos-10, obj.yPos)].playerHandling = HANDLING::SPECIAL;}}
	 if (obj.special == 9 && jjDifficulty >= 2){jjObjects[jjAddObject(OBJECT::BILSY, obj.xPos, obj.yPos)].playerHandling = HANDLING::SPECIAL;}
	 if (obj.special == 9 && jjDifficulty == 1){jjObjects[jjAddObject(OBJECT::BAT, obj.xPos, obj.yPos+120)].playerHandling = HANDLING::SPECIAL;}
	 if (obj.special == 9 && jjDifficulty == 0){jjAddObject(OBJECT::CARROT, obj.xPos, obj.yPos+140);}
	 //if (obj.special == 10){jjObjects[jjAddObject(OBJECT::FENCER, obj.xPos, obj.yPos)]; if(jjDifficulty >= 2){jjObjects[jjAddObject(OBJECT::FENCER, obj.xPos+10, obj.yPos)]; jjObjects[jjAddObject(OBJECT::FENCER, obj.xPos-10, obj.yPos)];}}
	 if (obj.special == 10){jjObjects[jjAddObject(OBJECT::FENCER, obj.xPos, obj.yPos)].playerHandling = HANDLING::SPECIAL;
	  if(jjDifficulty >= 2){jjObjects[jjAddObject(OBJECT::FENCER, obj.xPos+10, obj.yPos)].playerHandling = HANDLING::SPECIAL;}}
	 //reset cooldown
	 obj.age = 180;
	 if (jjDifficulty == 2){obj.age = 150;}
	 if (jjDifficulty >= 3){obj.age = 120;}
	 //select next move
	 obj.special += 1;
	 //progress towards next carrot
	 obj.var[2] = obj.var[2]-1;
	 //hurt boss
	 obj.energy -= 1;
	}
	//loop attack pattern
	if (obj.special >= 11){
	 obj.special = 5;
	}
	if (obj.var[2] <= 0){
	 if (jjDifficulty <= 2){jjAddObject(OBJECT::CARROT, obj.xPos, obj.yPos+140);}
	 if (jjDifficulty >= 3){jjAddObject(OBJECT::SEEKERAMMO3, obj.xPos, obj.yPos+140);}
	 obj.var[2] = 10;
	}
	//end boss
	if (obj.energy <= 0){obj.bulletHandling = HANDLING::DESTROYBULLET; obj.state = STATE::KILL;}
	//turn around
    if (obj.xPos <= 495*32){obj.var[0]=1;}
	if (obj.xPos >= 506*32){obj.var[0]=0;}
	//move
	if (obj.var[0] == 0){obj.xPos -=1;}
	if (obj.var[0] == 1){obj.xPos +=1;}
	
	//set reticle height
	if (obj.special == 0 || obj.special == 3 || obj.special == 5 || obj.special == 7 || obj.special == 8 && obj.special != 9){obj.var[1]=230;}
	if (obj.special == 1 || obj.special == 2 || obj.special == 4 || obj.special == 6){obj.var[1]=120;}
	if (obj.special == 9 && jjDifficulty >= 2){obj.var[1]=230;}
	if (obj.special == 9 && jjDifficulty == 1){obj.var[1]=120;}
	if (obj.special == 9 && jjDifficulty == 0){obj.var[1]=9001;}
	//display reticle
	if (obj.age < 45 && obj.age % 6 <= 2){jjDrawSprite(obj.xPos, obj.yPos+obj.var[1], ANIM::PLUS_RETICLES, 2, 0, 0);}
	//attempted work-around for killing bosses, this works about half the time
	for (int i = 1; i < jjObjectCount; i++){
	 jjOBJ@ o = jjObjects[i];
	 if (o.state == STATE::DONE){jjDeleteObject(o.objectID);}
	}
  default:
   //empty
 }
}
Anybody have any ideas on how to fix the problems I've run into?

EDIT: Upon thinking about it further, I'm starting to think the easiest solution would be to disable Jazz's uppercut and Spaz's kick during the boss battle. Any easy way to go about doing that?

EDIT 2: I managed to figure it out on my own, never mind.
__________________
Words. More words. Line. Insert text here. Placeholder. Lorem ipsum dolor sit amet. Uh... I guess I should put something proper here one day.
¯\_(?)_/¯

Last edited by happygreenfrog; Jan 22, 2018 at 12:57 PM.
DennisKainz DennisKainz's Avatar

JCF Member

Joined: Dec 2005

Posts: 407

DennisKainz is notorious for his worthless posts

Jan 31, 2019, 12:24 PM
DennisKainz is offline
Reply With Quote
I was going to upload a small mutator that allows players to grab ledges and lift themselves on them (as in Earthworm Jim and Commander Keen). However, there are a few problems...
Here's the script:
Code:
array hang = {0, 0, 0, 0};
array hangLeft = {false, false, false, false};
array hangRight = {false, false, false, false};

void onMain() {
	for (uint i = 0; i < 3; i++) {
		jjPLAYER@ player = jjLocalPlayers[i];
		if (player.isLocal) {
			if (player.ySpeed > 0 && hangLeft[i] == false && hangRight[i] == false)
				{
				if (!jjMaskedPixel(player.xPos - 16, player.yPos - 16) && jjMaskedVLine(player.xPos - 16, player.yPos - 14, 8) && player.direction < 0)
					{
					hang[i] = 1;
					hangLeft[i] = true;
					}
				if (!jjMaskedPixel(player.xPos + 16, player.yPos - 16) && jjMaskedVLine(player.xPos + 16, player.yPos - 14, 8) && player.direction > 0)
					{
					hang[i] = 1;
					hangRight[i] = true;
					}
				}
			if (hang[i] > 0)
				{
				player.invisibility = true;
				hang[i] = hang[i] + 1;
				if (hangLeft[i] == true)
					{
					player.xPos = player.xPos - 0.875;
					jjDrawSprite(player.xPos, player.yPos, ANIM::JAZZ, 20, 0 + (hang[i] / 3), -1, SPRITE::NORMAL, 0);
					}
				if (hangRight[i] == true)
					{
					player.xPos = player.xPos + 0.875;
					jjDrawSprite(player.xPos, player.yPos, ANIM::JAZZ, 20, 0 + (hang[i] / 3), 1, SPRITE::NORMAL, 0);
					}
				player.yPos = player.yPos - 1;
				player.xSpeed = 0;
				player.ySpeed = 0;
				}
			if (hang[i] > 30 && !jjMaskedPixel(player.xPos, player.yPos + 22))
				{
				player.invisibility = false;
				hang[i] = 0;
				hangLeft[i] = false;
				hangRight[i] = false;
				}
			}
		}
	}
For now, I only made it for Jazz.
I can't find a way to determine the animation of the player itself, so I had to make the player invisible and draw the sprite in front of him instead. This gives the player the default skin color while he's hanging, which is wrong. I need a way to determine the player's current animation, rather than drawing upon him.
Oh, right! There also is this problem with the RABBIT::Anim where I simply can't find the one corresponding to Jazz grabbing the ledge. I had to use the numeral value, which probably differs between 1.23 and 1.24.
__________________
Sincerely,
Dennis

Last edited by DennisKainz; Jan 31, 2019 at 01:31 PM. Reason: I found the way to use arrays for splitscreeners.
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Jan 31, 2019, 08:20 PM
Violet CLM is offline
Reply With Quote
That's a neat idea for a mutator!
Quote:
Originally Posted by DennisKainz View Post
I need a way to determine the player's current animation, rather than drawing upon him.
You can't do this yet, because the rabbit animation code is sufficiently complicated that we haven't come up with a good API for messing with it. But you should be able to solve your problem in the short-term by using SPRITE::PLAYER, player.playerID instead of SPRITE::NORMAL, 0.
Quote:
Oh, right! There also is this problem with the RABBIT::Anim where I simply can't find the one corresponding to Jazz grabbing the ledge. I had to use the numeral value, which probably differs between 1.23 and 1.24.
The ledge-climbing animations were removed in 1.24, which is why they don't have a RABBIT::Anim entry (and why Lori doesn't have those animations at all).
__________________
headshot2018 headshot2018's Avatar

JCF Member

Joined: Jan 1970

Posts: 2

headshot2018 has disabled reputation

Feb 7, 2019, 12:10 PM
headshot2018 is offline
Reply With Quote
hey, quick question.

is it possible to draw a custom scoreboard HUD in non-coop gametypes like CTF or battle?
i've been trying to define "bool onDrawScore()", but that seems to only alter the co-op score. is it possible?

thank you in advance.
Seren Seren's Avatar

JCF Member

Joined: Feb 2010

Posts: 825

Seren is a name known to allSeren is a name known to allSeren is a name known to allSeren is a name known to allSeren is a name known to allSeren is a name known to all

Feb 7, 2019, 02:30 PM
Seren is offline
Reply With Quote
At present, the score display in game modes other than SP and coop may not be altered, aside from drastic measures such as overwriting font sprites, which certainly wouldn't be limited in scope to said display and would probably quite negatively affect the game. Remaining HUD functions such as onDrawAmmo may still be used to display other information beside the usual.
__________________

I am an official JJ2+ programmer and this has been an official JJ2+ statement.
headshot2018 headshot2018's Avatar

JCF Member

Joined: Jan 1970

Posts: 2

headshot2018 has disabled reputation

Feb 7, 2019, 04:01 PM
headshot2018 is offline
Reply With Quote
oh... that's a bummer.

oh well, i'm gonna hold off on my project until it's possible to draw over CTF score, i guess.
thank you again.
__________________
http://teeworlds.com http://ddnet.tw
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Jan 28, 2020, 12:47 AM
chandie is offline
Reply With Quote
I'd like to use a regular enemy as a boss. I don't intend to change his behavior. I only change his sprites with tiles or replace them with JJ1Enemies sometimes.

I tried creating a behavior for the enemy and implement the if code below but it doesn't work.

if ( boss.energy==0&&++boss.counter>2300)
jjEventSet(play.xPos / 32, play.yPos / 32, AREA::EOL);

Is there a practical way of putting a health bar and an automatic EOL event when his energy is 0?

I am pretty new at scripting btw. Treat me as a noobie
Seren Seren's Avatar

JCF Member

Joined: Feb 2010

Posts: 825

Seren is a name known to allSeren is a name known to allSeren is a name known to allSeren is a name known to allSeren is a name known to allSeren is a name known to all

Jan 28, 2020, 06:25 AM
Seren is offline
Reply With Quote
Quote:
Originally Posted by chandie View Post
Is there a practical way of putting a health bar and an automatic EOL event when his energy is 0?
Hi! Awesome that you're learning JJ2+ scripting. The scripting documentation describes several entities that should help you.

A boss health bar is drawn for players if their bossActivated is true and boss refers to an object by its ID. There's also an accompanying activateBoss method, or you could use the traditional Activate Boss event. See especially the documentation for boss, as it describes how the health bar's size is determined.

Most JJ2 boss behaviors work the following way: the boss starts out inactive and its behavior has a loop over all players that checks if any active player's bossActivated is true. If it is, the boss changes its state to something else, indicating that it's now active. Then, whenever the boss is active, it runs a loop that sets all players' boss to itself. This results in the health bar being drawn.

When the boss is defeated and its energy is 0, the game doesn't despawn it, and instead (much like you, it seems) uses its behavior as a countdown to end the level. First, when the counter is 0, it might display some message much like showText. Then it waits between 4 and 6.5 seconds before it proceeds to the next level, depending on whether the player is firing bullets, for some weird reason (maybe under the assumption that if they're firing, they're getting impatient? Either way it's currently a speedrun strat to keep shooting after defeating a boss). Your magic number of 2300 seems pretty big as, at 70 ticks per second, that corresponds to over half a minute. Your strategy of spawning an EOL event seems valid, but it's generally a better idea to call jjNxt with default parameters.

To sum up the order of events in a typical boss fight:
  1. The boss is inactive.
  2. The player touches an Activate Boss event. This sets their bossActivated to true.
  3. The dormant boss detects that there's a player with an active boss fight so it wakes up.
  4. The boss sets all players' boss property to itself. This lets the health bar know what size it should have.
  5. Players defeat the boss. The boss behavior may display a text message.
  6. After a few seconds, the boss behavior ends the current level.
__________________

I am an official JJ2+ programmer and this has been an official JJ2+ statement.
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Jan 28, 2020, 07:57 AM
Violet CLM is offline
Reply With Quote
One possible additional consideration is your boss.energy==0 condition. Several weapons do more than 1 damage, so it's easy for an enemy to end up with a negative energy number instead of exactly 0. (Some standard enemies/bosses check for this and immediately set energy to 0, others set it to -1, and probably others don't do anything at all.)
__________________
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Jan 29, 2020, 03:41 AM
chandie is offline
Reply With Quote
Thumbs up JJNxt for the end boss

Quote:
Originally Posted by Seren View Post
A boss health bar is drawn for players if their bossActivated is true and boss refers to an object by its ID. There's also an accompanying activateBoss method, or you could use the traditional Activate Boss event. See especially the documentation for boss, as it describes how the health bar's size is determined.
Quote:
Originally Posted by Violet CLM View Post
One possible additional consideration is your boss.energy==0 condition. Several weapons do more than 1 damage, so it's easy for an enemy to end up with a negative energy number instead of exactly 0. (Some standard enemies/bosses check for this and immediately set energy to 0, others set it to -1, and probably others don't do anything at all.)
Thank you for the advices. I've managed to make the boss work. However the script stops working when I try to end the level automatically. I think what I've been doing wrong is not to figure where to implement the jjNXT. Here's my code. I've tried to create a JJ1 Turtle Goon Boss, like the one in Diamondus Secret.

Code:
#include "Jazz1Enemies v05.asc"
#include "Resize v11.asc"
#include "TrueColor v13.asc"
bool Boss=false; 

void onLevelLoad() {
	Jazz1::MakeEnemy(OBJECT::NORMTURTLE, Jazz1::Enemies::Diamondus_TurtleGoon, true);
	jjObjectPresets[OBJECT::NORMTURTLE].behavior = NORMTURTBOSS;
	jjObjectPresets[OBJECT::NORMTURTLE].energy = 100;
	jjObjectPresets[OBJECT::NORMTURTLE].points = 200;
	}

void onFunction0(jjPLAYER@ play) {
		Boss=true;
play.boss=jjAddObject(OBJECT::NORMTURTLE, 37*32, 54*32);
	}

void NORMTURTBOSS(jjOBJ@ boss) {
		jjPLAYER@ play = jjLocalPlayers[0];
	if (boss.energy==0&&++boss.counter>2300)  
			jjNxt(bool warp = false, bool fast = false);
	}
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Jan 29, 2020, 04:42 PM
Violet CLM is offline
Reply With Quote
The main problem you're experiencing is this line: jjNxt(bool warp = false, bool fast = false); Those bits inside the parentheses are supposed to indicate the default values for those arguments, meaning that if you just type jjNxt(); it'll be interpreted the same as if you had typed jjNxt(false, false);. Typing out the parameter names (and types) doesn't help.

The next thing that's going to bite you is when you write jjObjectPresets[OBJECT::NORMTURTLE].behavior = NORMTURTBOSS; you are getting rid of the previous behavior, which included code for moving the object, drawing the object, and handling player collision and bullet collision.
__________________
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Jan 30, 2020, 04:21 AM
chandie is offline
Reply With Quote
OK. Solved the problem. But sadly it doesn't work with JJ1 Enemies function. Because when I remove the JJ1Enemies function it works fine. It's main code probably considers it as a normal enemy and the counter does not start. So the level does not end. I tried removing the counter problem by trying to end the level when his energy=1 but it didn't work either.
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Feb 10, 2020, 11:18 PM
Violet CLM is offline
Reply With Quote
Very delayed response, but I don't think I can tell exactly what you're doing from your description. Troubleshooting is generally easiest in response to actual code, like in your previous post, preferably the minimum amount of code that demonstrates the problem in question. What does "works fine" look like in code? What does not removing the JJ1Enemies function look like in code?
__________________
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Mar 6, 2020, 06:33 AM
chandie is offline
Reply With Quote
Turtle Boss

And another delayed response from me
It's been a while and I didn't keep the code. However I've tried using Normal Turtle animations and a new behavior in order to make it work and it helped. Here's the thing:

Code:
bool boss=false;


			void onLevelLoad()  {
	
	jjObjectPresets[OBJECT::PURPLEGEM].points = 2000;		
	jjObjectPresets[OBJECT::NORMTURTLE].behavior = TURTBOSS;
	jjObjectPresets[OBJECT::NORMTURTLE].points = 5000;
	jjObjectPresets[OBJECT::NORMTURTLE].energy = 50;
	jjObjectPresets[OBJECT::NORMTURTLE].bulletHandling = HANDLING::HURTBYBULLET;
	jjObjectPresets[OBJECT::NORMTURTLE].state = STATE::WALK;

	}
	
	

	void onFunction0(jjPLAYER@ play) {
	play.activateBoss();
	boss=true;
	{play.boss=jjAddObject(OBJECT::NORMTURTLE, 38*32, 55*32);}
		jjMusicLoad("boss.s3m");
		play.activateBoss(true);

	}	

	
		void TURTBOSS(jjOBJ@ boss) {

		jjPLAYER@ play = jjLocalPlayers[0];
	
			
	if ( boss.energy==0) 
	{	
		boss.counter==0;
		boss.counter += 0; 
		boss.determineCurAnim(ANIM::TURTLE, 7);
		boss.determineCurFrame();
		boss.frameID = boss.counter/56;
		jjDrawSpriteFromCurFrame(boss.xPos, boss.yPos, boss.curFrame, boss.direction, SPRITE::NORMAL);
		++boss.counter;
	
	if ( boss.energy==0&&++boss.counter>20)  
			jjEventSet(play.xPos / 32, play.yPos / 32, AREA::EOL);
	}
	
	if ((boss.xPos > play.xPos+3 || boss.xPos < play.xPos-3)&& boss.state != STATE::KILL)
	{boss.direction = (play.xPos < boss.xPos) ? +1 : +1;}
	else 
				{
				boss.direction==1;}
	if (boss.yPos < 54*32)
	{boss.yPos = boss.yPos;}
	if (boss.xPos > 24*32)
	{boss.xPos = boss.xPos;}
		switch (boss.state) {
		
	 case STATE::WALK:
        boss.behave(BEHAVIOR::WALKINGENEMY);
        {
		if (boss.counter >= 180)
		{boss.counter = 0; }
  
                boss.counter++;
                if (u==0||u==1)
                {if (++boss.counter < 180){
                        boss.xSpeed= (1+1)*boss.direction;
                        boss.determineCurAnim(ANIM::TURTLE, 7);
                        if (boss.counter == 10){
                        jjSample(boss.xPos, boss.yPos, SOUND::INTRO_GREN3);}
                        if (boss.justHit == 0)
                        {jjDrawSpriteFromCurFrame(boss.xPos, boss.yPos, boss.curFrame, boss.direction, SPRITE::NORMAL);}
                        boss.determineCurFrame();
                        //boss.frameID = boss.counter/10;
						}
                        
				break;		
						
						
  	
                        } }
						case STATE::FREEZE:
			if (boss.freeze > 0) {
				boss.draw();
				boss.freeze -= 4;
			}
			if (boss.freeze < 4) {
				boss.unfreeze(0);
				boss.state = boss.oldState;
			}
			
			break;
	case STATE::KILL:
	case STATE::DEACTIVATE:
		boss.deactivate();
		break;

		
										
						}
					//if (boss.justHit == 0)
		//jjDrawSpriteFromCurFrame(boss.xPos, boss.yPos, boss.curFrame, boss.direction, SPRITE::NORMAL);
		//else jjDrawSpriteFromCurFrame(boss.xPos, boss.yPos, boss.curFrame, boss.direction, SPRITE::SINGLECOLOR,15);			
						}
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Mar 7, 2020, 10:40 PM
chandie is offline
Reply With Quote
Have another question though. Is there a way to combine water level event with destruct scenery? I mean a shootable water level event like the ones in dreempipes.
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Mar 9, 2020, 10:55 PM
Violet CLM is offline
Reply With Quote
Quote:
Originally Posted by chandie View Post
However I've tried using Normal Turtle animations and a new behavior in order to make it work and it helped.
Broadly speaking, it seems to be working, at least once I get rid of the if (u==0||u==1) line which I assume refers to a variable you didn't include in that snippet. You are running into an issue where killing it using a physical attack fails to cause the level to end, but jObjectPresets[OBJECT::NORMTURTLE].playerHandling = HANDLING::ENEMY; should fix that. Also you probably don't want STATE::KILL to call boss.deactivate().
Quote:
I mean a shootable water level event like the ones in dreempipes.
Sure... jjSetWaterLevel exists. You may need to figure out jjBEHAVIORINTERFACE and its onObjectHit method, though, since I'm guessing you want the object to react only to being shot by bullets, not to being buttstomped.
__________________
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Mar 10, 2020, 07:09 AM
chandie is offline
Reply With Quote
Walking Enemy

Quote:
Originally Posted by Violet CLM View Post
You are running into an issue where killing it using a physical attack fails to cause the level to end, but jObjectPresets[OBJECT::NORMTURTLE].playerHandling = HANDLING::ENEMY; should fix that. Also you probably don't want STATE::KILL to call boss.deactivate().
Thanks for the tips Violet. Yeah with playerHandling command EOL works with physical attack.


Quote:
Originally Posted by Violet CLM View Post
Sure... jjSetWaterLevel exists. You may need to figure out nObjectHit">jjBEHAVIORINTERFACE and its onObjectHit method, though, since I'm guessing you want the object to react only to being shot by bullets, not to being buttstomped.
I solved jjSetWaterLevel with switchtrigger. so it works fine with a trigger zone. but objecthit with bullet seems a little more complicated but I'll keep working on it. Yeah it shouldn't work by buttstomping.


And I have a one last question. I am trying to create a walking enemy from Frog animations. It works but the animation plays under the ground sprite. Here's the code:

Code:
void onLevelLoad() {

	jjObjectPresets[OBJECT::LIZARD].behavior = Frog;
	jjObjectPresets[OBJECT::LIZARD].bulletHandling = HANDLING::HURTBYBULLET;
	jjObjectPresets[OBJECT::LIZARD].playerHandling = HANDLING::ENEMY;

}




void Frog(jjOBJ@ obj) {
	obj.behave(BEHAVIOR::WALKINGENEMY, false);
	obj.determineCurAnim(ANIM::FROG, 12);
	obj.determineCurFrame();
	obj.putOnGround(true);


jjDrawSpriteFromCurFrame(obj.xPos, obj.yPos, obj.curFrame, obj.direction, SPRITE::NORMAL, 0);


}
I've tried some other things like setting WALKINENEMY value to true but it didn't change anything. I've also try adding some negative values to obj.yPos in jjDrawSpriteFromCurFrame. It helps visually. But functionally the enemy is still under the ground. Any suggestions ar highly appreciated. Thank you.
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Mar 12, 2020, 04:55 PM
Violet CLM is offline
Reply With Quote
First off, you could simplify all that like this:
Code:
void onLevelLoad() {
	jjObjectPresets[OBJECT::LIZARD].determineCurAnim(ANIM::FROG, jjIsTSF ? 12 : 11);
}
You're running into the problem that BEHAVIOR::WALKINGENEMY is one of the only parts of the game that cares about jjANIMFRAME::coldSpotY, but the frog sprites are one of the only land-based animations that don't have that property defined. Walking enemies try to align their hotSpotY point to the surface of the ground they're on, but that number defaults to 0, which is the top of the sprite. Just move it upwards until it looks better:
Code:
	jjANIMATION@ frogWalkAnim = jjAnimations[jjObjectPresets[OBJECT::LIZARD].curAnim];
	for (uint i = 0; i < frogWalkAnim.frameCount; ++i)
		jjAnimFrames[frogWalkAnim.firstFrame + i].coldSpotY = -25;
__________________
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Mar 12, 2020, 11:04 PM
chandie is offline
Reply With Quote
Solved

Quote:
Originally Posted by Violet CLM View Post
First off, you could simplify all that like this:
Code:
void onLevelLoad() {
	jjObjectPresets[OBJECT::LIZARD].determineCurAnim(ANIM::FROG, jjIsTSF ? 12 : 11);
}
You're running into the problem that BEHAVIOR::WALKINGENEMY is one of the only parts of the game that cares about jjANIMFRAME::coldSpotY, but the frog sprites are one of the only land-based animations that don't have that property defined. Walking enemies try to align their hotSpotY point to the surface of the ground they're on, but that number defaults to 0, which is the top of the sprite. Just move it upwards until it looks better:
Code:
	jjANIMATION@ frogWalkAnim = jjAnimations[jjObjectPresets[OBJECT::LIZARD].curAnim];
	for (uint i = 0; i < frogWalkAnim.frameCount; ++i)
		jjAnimFrames[frogWalkAnim.firstFrame + i].coldSpotY = -25;
Solved. thanks a lot
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Mar 19, 2020, 02:00 AM
chandie is offline
Reply With Quote
I am trying to increase/decrease the player's x speed like this:
if(p.keyRight)
p.xSpeed = 20;

It works with void onPlayer but not in bool onObjectHit or void onFunction Any suggestions?
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Mar 21, 2020, 08:40 AM
Violet CLM is offline
Reply With Quote
There are a couple things you might be running into. One is that JJ2 is of course always changing the player's xSpeed on its own, so if you make a single short-term change, in response to something like one of the onFunction# hooks, then that change is not going to last very long before JJ2's normal speed handling takes over. But onPlayer works constantly instead of as a one-time thing, so naturally it's going to have more visible impact. The other is that p is deprecated and may not (should not) work in all situations, and you should use the actual jjPLAYER@ argument available to onObjectHit and onFunction# instead.
__________________
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Mar 22, 2020, 10:11 PM
chandie is offline
Reply With Quote
So even if I try with jjPLAYER I can’t make a constant speed change.

I could change the speed like this though, again with onPlayer.

if(play.xPos>27 && play.yPos>43)
if(play.keyRight) {
play.xSpeed = 10;
}
if(play.keyLeft) {
play.xSpeed = -10;
}
play.keyRun = true;
}

But that’s not the exact effect I want. I’d like to make a constant speed up (at least for ten seconds or so) regardless of the player pos
Violet CLM Violet CLM's Avatar

JCF Éminence Grise

Joined: Mar 2001

Posts: 10,719

Violet CLM has disabled reputation

Mar 23, 2020, 06:41 AM
Violet CLM is offline
Reply With Quote
Ah, okay, I see what you mean. Try something like this:
Code:
int SpeedUpUntil = 0;
void onPlayer(jjPLAYER@ play) {
  if (SpeedUpUntil > jjGameTicks) {
    //adjust player's xspeed and such in here
  }
}
Then whenever you want to turn on the speed up effect, in an onObjectHit or whatever, set SpeedUpUntil = jjGameTicks + 10 * 70; where 10 is the number of seconds you want the effect to last.

(if you want to support cooperative, SpeedUpUntil should be an array<int> instead of a int.)
__________________
chandie

JCF Member

Joined: Jan 1970

Posts: 11

chandie has disabled reputation

Mar 24, 2020, 03:20 AM
chandie is offline
Reply With Quote
Worked like a charm. Thank you
Reply

Tags
angelscript, code, jcs, request

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is On

Forum Jump

All times are GMT -8. The time now is 11:43 AM.