Downloads containing STVutil.asc

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Custom HUD Spaz Electro Mutator N/A Download file

File preview

funcdef void TimerVDictionaryFunction(dictionary @);
class TimerV : jjBEHAVIORINTERFACE
{
	TimerV(int time, jjVOIDFUNC @callback)
	{
		@_callback = @callback;
		_start(time);
	}
	TimerV(int time, TimerVDictionaryFunction @callback, dictionary @arguments)
	{
		@_callbackWithArguments = @callback;
		@_arguments = @arguments;
		_start(time);
	}
	bool get_Active() const
	{
		return cast<jjBEHAVIORINTERFACE @>(_object.behavior) is this;
	}
	int get_Elapsed() const
	{
		if (Active)
			return _object.age;
		return -1;
	}
	int get_Total() const
	{
		if (Active)
			return _object.counter;
		return -1;
	}
	int get_Remaining() const
	{
		if (Active)
			return _object.counter - _object.age;
		return -1;
	}
	bool Stop()
	{
		if (Active && _object.age < _object.counter)
		{
			_object.delete();
			return true;
		}
		return false;
	}
	bool Paused = false;

private
	jjVOIDFUNC @_callback = null;
private
	TimerVDictionaryFunction @_callbackWithArguments;
private
	dictionary @_arguments;
private
	jjOBJ @_object;
private
	int _startTime;
private
	void _start(int time)
	{
		if (time > 0)
		{
			@_object = jjObjects[jjAddObject(OBJECT::BEES, -1000, -1000, 0, CREATOR::OBJECT, BEHAVIOR::BEES)];
			_object.behavior = this;
			_object.counter = time;
			_object.age = 0;
			_object.playerHandling = HANDLING::PARTICLE;
			_object.deactivates = false;
			_startTime = jjGameTicks;
		}
		else
		{
			@_object = jjObjects[0]; // avoid null pointer access
			_pickCallback();
		}
	}
private
	void onBehave(jjOBJ @obj)
	{
		if (!Paused && jjGameTicks > _startTime && obj is _object && ++_object.age >= _object.counter)
		{
			_pickCallback();
			_object.delete();
		}
	}
private
	void _pickCallback()
	{
		if (_callback !is null)
			_callback();
		else
			_callbackWithArguments(_arguments);
	}
}

class Key
{
	string id;
	uint code;

	Key(string id, uint code)
	{
		this.id = id;
		this.code = code;
	}
}

jjRNG stvutilRNG = jjRNG();
jjTEXTAPPEARANCE normalTextAppearance = jjTEXTAPPEARANCE(STRING::NORMAL);

array<Key @>
	KEYS = {
		Key("F2", 0x71),			 // 0
		Key("F5", 0x74),			 // 1
		Key("F6", 0x75),			 // 2
		Key("F7", 0x76),			 // 3
		Key("F10", 0x79),			 // 4
		Key("F11", 0x7A),			 // 5
		Key("Insert", 0x2D),		 // 6
		Key("Home", 0x24),			 // 7
		Key("PageUp", 0x21),		 // 8
		Key("Delete", 0x2E),		 // 9
		Key("End", 0x23),			 // 10
		Key("PageDown", 0x22),		 // 11
		Key("Colon", 0xBA),			 // 12
		Key("QuotationMark", 0xDE),	 // 13
		Key("Backspace", 0x08),		 // 14
		Key("Backslash", 0xDC),		 // 15
		Key("Backquote", 0xC0),		 // 16
		Key("BracketLeft", 0xDB),	 // 17
		Key("BracketRight", 0xDD),	 // 18
		Key("Comma", 0xBC),			 // 19
		Key("Minus", 0xBD),			 // 20
		Key("Period", 0xBE),		 // 21
		Key("Slash", 0xBF),			 // 22
		Key("Plus", 0xBB),			 // 23
		Key("Shift", 0x10),			 // 24
		Key("Alt", 0x12),			 // 25
		Key("Control", 0x11),		 // 26
		Key("Tab", 0x09),			 // 27
		Key("Caps Lock", 0x14),		 // 28
		Key("Space", 0x20),			 // 29
		Key("Up", 0x26),			 // 30
		Key("Down", 0x28),			 // 31
		Key("Left", 0x25),			 // 32
		Key("Right", 0x27),			 // 33
		Key("NumpadSlash", 0x6F),	 // 34
		Key("NumpadAsterisk", 0x6A), // 35
		Key("NumpadMinus", 0x6D),	 // 36
		Key("NumpadPlus", 0x6B),	 // 37
		Key("Numpad1", 0x61),		 // 38
		Key("Numpad2", 0x62),		 // 39
		Key("Numpad3", 0x63),		 // 40
		Key("Numpad4", 0x64),		 // 41
		Key("Numpad5", 0x65),		 // 42
		Key("Numpad6", 0x66),		 // 43
		Key("Numpad7", 0x67),		 // 44
		Key("Numpad8", 0x68),		 // 45
		Key("Numpad9", 0x69),		 // 46
		Key("Numpad0", 0x60),		 // 47
		Key("NumpadPeriod", 0x6E),	 // 48
		Key("0", 0x30),				 // 49
		Key("1", 0x31),				 // 50
		Key("2", 0x32),				 // 51
		Key("3", 0x33),				 // 52
		Key("4", 0x34),				 // 53
		Key("5", 0x35),				 // 54
		Key("6", 0x36),				 // 55
		Key("7", 0x37),				 // 56
		Key("8", 0x38),				 // 57
		Key("9", 0x39),				 // 58
		Key("A", 0x41),				 // 59
		Key("B", 0x42),				 // 60
		Key("C", 0x43),				 // 61
		Key("D", 0x44),				 // 62
		Key("E", 0x45),				 // 63
		Key("F", 0x46),				 // 64
		Key("G", 0x47),				 // 65
		Key("H", 0x48),				 // 66
		Key("I", 0x49),				 // 67
		Key("J", 0x4A),				 // 68
		Key("K", 0x4B),				 // 69
		Key("L", 0x4C),				 // 70
		Key("M", 0x4D),				 // 71
		Key("N", 0x4E),				 // 72
		Key("O", 0x4F),				 // 73
		Key("P", 0x50),				 // 74
		Key("Q", 0x51),				 // 75
		Key("R", 0x52),				 // 76
		Key("S", 0x53),				 // 77
		Key("T", 0x54),				 // 78
		Key("U", 0x55),				 // 79
		Key("V", 0x56),				 // 80
		Key("W", 0x57),				 // 81
		Key("X", 0x58),				 // 82
		Key("Y", 0x59),				 // 83
		Key("Z", 0x5A),				 // 84
		Key("Enter", 0x0D),			 // 85
};

class AnimatedSprite
{
	int id;
	float frame;
	int frame_count;
	int x;
	int y;
	double anim_speed;
	bool can_reverse;
	bool reverse = false;
	bool visible = true;
	ANIM::Set animSet = ANIM::AMMO;
	SPRITE::Mode spriteMode = SPRITE::NORMAL;
	int spriteModeParam = 123;
	SPRITE::Direction direction = SPRITE::FLIPNONE;

	AnimatedSprite(int id, int frame, int x, int y, double anim_speed, bool can_reverse)
	{
		this.id = id;
		this.frame = frame;
		this.x = x;
		this.y = y;
		this.anim_speed = anim_speed;
		this.can_reverse = can_reverse;
		this.frame_count = jjAnimations[jjAnimSets[animSet].firstAnim + id].frameCount;
	}

	void setVisible(bool visible)
	{
		this.visible = visible;
	}

	void setAnimSet(ANIM::Set animSet)
	{
		this.animSet = animSet;
		this.frame_count = jjAnimations[jjAnimSets[animSet].firstAnim + id].frameCount;
	}

	void setId(uint id)
	{
		this.id = id;
		this.frame_count = jjAnimations[jjAnimSets[this.animSet].firstAnim + id].frameCount;
	}

	void update()
	{
		if (this.reverse == false)
		{
			this.frame += this.anim_speed;
		}
		else
		{
			this.frame -= this.anim_speed;
		}

		if (this.frame > this.frame_count)
		{
			if (this.can_reverse == true)
			{
				this.reverse = not this.reverse;
			}
			else
			{
				this.frame = 0;
			}
		}

		if (this.frame <= 0)
		{
			if (this.can_reverse == true)
			{
				this.reverse = not this.reverse;
			}
			else
			{
				this.frame = 0;
			}
		}
	}

	void draw(jjCANVAS @canvas)
	{
		if (this.visible)
		{
			canvas.drawSprite(this.x, this.y, this.animSet, this.id, int(this.frame), this.direction, this.spriteMode, this.spriteModeParam);
		}
	}
}

class Vector2
{
	uint x;
	uint y;

	Vector2(int x, int y)
	{
		this.x = x;
		this.y = y;
	}

	float magnitude(Vector2 @otherVector)
	{
		return sqrt((this.x + this.y) - (otherVector.x + otherVector.y));
	}
};

class Box
{
	uint x;
	uint y;
	uint width;
	uint height;

	Box(uint x, uint y, uint width, uint height)
	{
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	void draw(jjCANVAS @canvas)
	{
		canvas.drawRectangle(this.x, this.y, this.width, this.height, 0, SPRITE::NORMAL, 0);
	}
}

// shouldClose: bool
funcdef bool DialogueChoiceCallback(DialogueChoice @, Dialogue @);
class DialogueChoice
{
	string text;
	int choiceId;
	DialogueChoiceCallback @callback;

	DialogueChoice(string text, int choiceId, DialogueChoiceCallback @callback)
	{
		this.text = text;
		this.choiceId = choiceId;
		@ this.callback = callback;
	}

	void draw(jjCANVAS @canvas, Box @box, string originalText, bool selected)
	{
		canvas.drawString(box.x, box.y + 40 + (choiceId * 45) + getStringHeight(originalText), (selected ? "|" : "") + "[" + formatInt(choiceId + 1) + "] " + this.text, STRING::MEDIUM, STRING::NORMAL, 0);
	}
}

class Dialogue
{
	string text;
	string textDisplayed;

	uint choice;

	int speed = 5;
	int x = 0;
	int y = 0;
	int lastCharTick = 0;

	bool visible = false;
	bool finished = false;
	bool can_skip = true;
	bool moreDialog = false;
	bool skipped = false;
	bool keyCooldown = false;
	bool characterLoaded = false;

	array<DialogueChoice @> choices;

	string character;

	int characterCID = 0;
	int characterScaleX = 1;
	int characterScaleY = 1;

	Box @box;
	Box @characterBox;

	Dialogue(string text, array<DialogueChoice @> choices)
	{
		this.text = text;
		this.x = 0;
		this.y = 0;
		this.choices = choices;
	}

	Dialogue(string text, array<DialogueChoice @> choices,
			 string character, int CID, Box @characterBox)
	{
		this.text = text;
		this.x = 0;
		this.y = 0;
		this.choices = choices;

		this.character = character;
		this.characterCID = CID;
		@ this.characterBox = characterBox;
	}

	void initialize()
	{
		@ this.box = Box(0, 0, jjGetStringWidth(this.text, STRING::MEDIUM, normalTextAppearance), 50 + (this.choices.length * 45));
		this.visible = true;
		this.finished = false;
		this.textDisplayed = "";
		this.lastCharTick = jjGameTicks;

		// (this.box.x) - uint(
		// 	jjAnimFrames[jjAnimations[jjAnimSets[ANIM::CUSTOM[this.characterCID]]]]
		// 		.width / 2.5
		// ),
		// jjResolutionHeight / 2,

		jjAnimSets[ANIM::CUSTOM[this.characterCID]].load(jjPIXELMAP(this.character),
														 this.characterBox.width, this.characterBox.height);

		this.characterLoaded = true;

		uint longestChoice = 1;

		for (uint choiceIndex = 0; choiceIndex < this.choices.length; choiceIndex++)
		{
			DialogueChoice @choice = this.choices[choiceIndex];

			if (getStringLength(choice.text) > longestChoice)
				longestChoice = getStringLength(choice.text);
		}

		this.box.width += (longestChoice * 45);
	}

	void reload()
	{
		@ this.box = Box(0, 0, jjGetStringWidth(this.text, STRING::MEDIUM, normalTextAppearance), 50 + (this.choices.length * 45));

		jjAnimSets[ANIM::CUSTOM[this.characterCID]].load(jjPIXELMAP(this.character),
														 this.characterBox.width, this.characterBox.height);

		this.characterLoaded = true;

		uint longestChoice = 1;

		for (uint choiceIndex = 0; choiceIndex < this.choices.length; choiceIndex++)
		{
			DialogueChoice @choice = this.choices[choiceIndex];

			if (getStringLength(choice.text) > longestChoice)
				longestChoice = getStringLength(choice.text);
		}

		this.box.width += (longestChoice * 45);
	}

	void loadNewCharacter(string character, int CID, Box @characterBox)
	{
		this.character = character;
		this.characterCID = CID;
		@ this.characterBox = characterBox;

		jjAnimSets[ANIM::CUSTOM[CID]].load(jjPIXELMAP(character),
										   characterBox.width, characterBox.height);
	}

	void draw(jjCANVAS @canvas)
	{
		if (this.visible)
		{
			this.box.x = jjResolutionWidth / 2 - (jjGetStringWidth(this.text, STRING::MEDIUM, normalTextAppearance)) / 2 + this.x;
			this.box.y = (jjResolutionHeight - 100 + this.y) - 25 - (this.choices.length * 25);
			this.box.draw(canvas);

			canvas.drawString(
				this.box.x,
				this.box.y + 10,
				this.textDisplayed, STRING::MEDIUM, STRING::NORMAL, 0);

			if (this.finished)
			{
				for (uint choiceIndex = 0; choiceIndex < this.choices.length; choiceIndex++)
				{
					DialogueChoice @choice = this.choices[choiceIndex];

					choice.draw(
						canvas,
						@ this.box,
						this.text,
						this.choice ==
							choiceIndex);
				}
			}

			if (this.moreDialog)
			{
				canvas.drawString(
					this.box.x + this.box.width - 25,
					this.box.y + 50,
					">>", STRING::SMALL, STRING::NORMAL, 0);
				// } else {
				//     canvas.drawString(
				//         this.box.x + this.box.width - 25,
				//         this.box.y + 50,
				//         ">", STRING::SMALL, STRING::NORMAL, 0);
			}

			if (this.characterLoaded)
			{
				canvas.drawResizedSprite(
					this.characterBox.x, this.characterBox.y,
					ANIM::CUSTOM[this.characterCID], 0, 0,
					this.characterScaleX, this.characterScaleY);
			}
		}
	}

	void update()
	{
		if (this.visible && this.lastCharTick + this.speed < jjGameTicks)
		{
			if (uint(jjGetStringWidth(this.text, STRING::MEDIUM, normalTextAppearance)) >= this.box.y)
			{
				this.textDisplayed = this.text.substr(0, this.textDisplayed.length() + 1) + "@";
			}
			else
			{
				this.textDisplayed = this.text.substr(0, this.textDisplayed.length() + 1);
			}

			if (getStringLength(this.textDisplayed) >= getStringLength(this.text))
			{
				this.finished = true;
			}

			lastCharTick = jjGameTicks;
		}
	}

	void input(jjPLAYER @player)
	{
		if (this.visible)
		{
			if (jjKey[KEYS[85].code] && this.keyCooldown == false)
			{
				if (this.finished || this.can_skip)
					this.end();

				if (!this.finished && this.can_skip)
					this.skipped = true;
			}

			if (this.choice < 0)
				this.choice = 0;
			if (this.choice > (this.choices.length - 2))
				this.choice = this.choices.length - 1;

			// up
			if (jjKey[KEYS[30].code])
			{
				// this is because if we get a negative value then the number is over 416k
				if (!(this.choice - 1 > this.choices.length + 1))
					this.choice -= 1;
			}

			// down
			if (jjKey[KEYS[31].code])
			{
				this.choice += 1;
			}

			this.keyCooldown = true;

			dictionary dict = {{"dialogue", @ this}};

			TimerV(
				80, function(this) {
					Dialogue @dialog;
					this.get("dialogue", @dialog);

					dialog.keyCooldown = false;
				},
				dict);
		}
	}

	void end()
	{
		if (this.choices.length != 0)
		{
			if (this.finished)
			{
				if (
					(this.choices[this.choice])
						.callback(
							@(this.choices[this.choice]), @ this))
				{
					this.visible = false;
				}

				return;
			}
		}

		this.visible = false;
		this.finished = true;
	}
}

// incomplete
enum RabbitState {
	IDLE,
	WALKING,
	RUNNING,
	JUMPING,
	FALLING,
	DYING,
	DEAD,
	SHOOTING,
	PUSHING,
	HURT
};

class RabbitFur {
	// this isn't RGBA but its instead of using "abcd"
	int r, g, b, a;

	RabbitFur(int r, int g, int b, int a) {
		this.r = r;
		this.g = g;
		this.b = b;
		this.a = a;
	}

	// use SPRITE::PLAYER and spriteParam as the result
	int emulateFur() {
		getFreePlayer().furSet(this.r, this.g, this.b, this.a);

		return getFreePlayer().clientID;
	}
}

class RabbitNPC {
	RabbitState state;
	string name;
	RabbitFur@ fur;

	RabbitNPC(string name, RabbitFur fur) {
		this.name = name;
		@this.fur  = fur;
		this.state = RabbitState::IDLE;
	}
}

jjPLAYER@ getFreePlayer() {
	for (int i = 0; i < 32; i++)
	{
		if (!jjPlayers[i].isActive)
			return jjPlayers[i];
	}

	return jjPlayers[1];
}

uint getStringLength(string str)
{
	string str2 = str;
	uint i = 0;

	while (i < str2.length())
	{
		if (str2[i] == "|"[0])
		{
			str2 = str2.substr(0, i) + str2.substr(i + 1);
		}

		i++;
	}

	return str2.length();
}

uint getStringHeight(string str)
{
	uint height = 0;

	for (uint i = 0; i < str.length(); i++)
	{
		if (str[i] == "@"[0])
		{
			height += 1;
		}
	}

	return height * 20;
}

bool parseBool(string str)
{
	if (str == "true" || str == "1" || str == "yes")
		return true;
	return false;
}

string formatBool(bool b)
{
	if (b)
		return "1";
	return "0";
}

string formatSize(STRING::Size size)
{
	if(size == STRING::SMALL) return "small";
	if(size == STRING::MEDIUM) return "medium";
	return "large";
}

STRING::Size parseSize(string size)
{
	if(size == "small") return STRING::SMALL;
	if(size == "medium") return STRING::MEDIUM;
	return STRING::LARGE;
}

string formatBoolToStringInteger(bool b)
{
	if (b)
		return "1";
	return "0";
}

CHAR::Char parseCharacter(string str)
{
	if (str == "SPAZ")
		return CHAR::SPAZ;
	if (str == "LORI")
		return CHAR::LORI;
	if (str == "BIRD")
		return CHAR::BIRD;
	if (str == "FROG")
		return CHAR::FROG;
	if (str == "BIRD2")
		return CHAR::BIRD2;

	return CHAR::JAZZ;
}

string formatCharacter(CHAR::Char charc)
{
	if (charc == CHAR::SPAZ)
		return "SPAZ";
	if (charc == CHAR::LORI)
		return "LORI";
	if (charc == CHAR::BIRD)
		return "BIRD";
	if (charc == CHAR::FROG)
		return "FROG";
	if (charc == CHAR::BIRD2)
		return "BIRD2";

	return "JAZZ";
}

GEM::Color parseGem(string str)
{
	if (str == "GREEN")
		return GEM::GREEN;
	if (str == "BLUE")
		return GEM::BLUE;
	if (str == "PURPLE")
		return GEM::PURPLE;

	return GEM::RED;
}

string formatGem(GEM::Color g)
{
	if (g == GEM::GREEN)
		return "GREEN";
	if (g == GEM::BLUE)
		return "BLUE";
	if (g == GEM::PURPLE)
		return "PURPLE";

	return "RED";
}

array<string> cloneStringArray(array<string> arr)
{
	array<string> newArray;

	for (uint itemIndex = 0; itemIndex < arr.length; itemIndex++)
	{
		newArray.insertLast(arr[itemIndex]);
	}

	return newArray;
}

int boolToInt(bool b)
{
	if (b)
		return 1;
	else
		return 0;
}

string getPipeColor(int pipeCount)
{
	string color = "white";

	switch (pipeCount)
	{
	case 1:
		color = "green";
		break;
	case 2:
		color = "red";
		break;
	case 3:
		color = "blue";
		break;
	case 4:
		color = "yellow";
		break;
	case 5:
		color = "pink";
		break;
	case 6:
		color = "white";
		break;
	case 7:
		color = "green";
		break;
	case 8:
		color = "cyan";
		break;
	}

	return color;
}

string restartPipePattern(string str)
{
	// fix this
	// w|g|r|b|y|p|w|g|c|(restart from g)
	// white-green-red-blue-yellow-pink-white(ish)-green-cyan(i think)

	int pipeCount = 0;
	string color = "white";

	for (uint i = 0; i < str.length(); i++)
	{
		if (str[i] == "|"[0])
		{
			pipeCount += 1;

			if (pipeCount > 8)
			{
				pipeCount = 1;
			}
		}
	}

	color = getPipeColor(pipeCount);

	while (color != "white")
	{
		str = str + "|";

		pipeCount += 1;

		if (pipeCount > 8)
		{
			pipeCount = 1;
		}
	}

	return str;
}

Key @isAnyKeyDown()
{
	Key @keyThatIsDown;

	for (uint i = 0; i < KEYS.length() - 1; i++)
	{
		Key @key = KEYS[i];

		if (jjKey[key.code])
		{
			@keyThatIsDown = key;
		}
	}

	return keyThatIsDown;
}

bool isKeyDown(Key @key)
{
	return jjKey[key.code];
}

Key @getKeyById(string id)
{
	Key @foundKey;

	for (uint i = 0; i < KEYS.length() - 1; i++)
	{
		Key @key = KEYS[i];

		if (key.id == id)
		{
			@foundKey = key;
			break;
		}
	}

	return foundKey;
}

int getRandomNumber(int min, int max)
{
	int num = stvutilRNG();

	if (num < 0)
		num *= -1;

	num = (num % (max - min + 1)) + min;

	return num;
}

int getPlayerCount()
{
	int count = 0;

	for (int i = 0; i < 32; i++)
	{
		if (jjPlayers[i].isActive)
			count++;
	}

	return count;
}

jjPLAYER @getRandomPlayer()
{
	jjPLAYER @user = jjPlayers[getRandomNumber(0, getPlayerCount())];

	if (user.isActive and !user.isOut)
		return user;
	else
		return getRandomPlayer();
}

funcdef void KeyPressCallback(uint);

array<bool> prevKeys(256, false);
array<KeyPressCallback@> keyPressCallbacks;

void updateKeys()
{
    for (uint keyCode = 0; keyCode < 256; keyCode++)
    {
        if (jjKey[keyCode] && !prevKeys[keyCode])
        {
            for (uint i = 0; i < keyPressCallbacks.length; i++)
            {
                keyPressCallbacks[i](keyCode);
            }
        }

        prevKeys[keyCode] = jjKey[keyCode];
    }
}