00:00
00:00
3p0ch
If you like hard games try my Daxolissian System series

plasmid @3p0ch

Cat

Scientist

Read the manual & try stuff

So Cal

Joined on 2/13/10

Level:
13
Exp Points:
1,628 / 1,880
Exp Rank:
38,701
Vote Power:
5.47 votes
Audio Scouts
1
Rank:
Portal Security
Global Rank:
23,820
Blams:
50
Saves:
369
B/P Bonus:
8%
Whistle:
Normal
Medals:
4,599
Supporter:
3y 2m 9d

3p0ch's News

Posted by 3p0ch - July 22nd, 2023


WARNING: Currently Godot 4 web exports apparently don't work on Apple (macOS and iOS) devices - see the notices in the Godot docs on web export. If that changes in the future, I'll plan to remove this warning. That said, I updated my NewGrounds API implementation to work with the new Godot 4 GDScript because Apple compatibility might someday be fixed and I know there are people using Godot 4 now anyway. If you're still early in your project, I'd recommend making it in Godot 3 (which is Apple compatible) instead of Godot 4 and using my Godot 3 NewGrounds API implementation.


If you're forging ahead with Godot 4, just copy/paste the code below and save it as ngio.gd in your Godot 4 project and set it to be an autoload.


extends Node

var app_id = "" # Put your app ID from NewGrounds here
var aes_key = "" # Put your AES-128/Base64 encryption key 
var show_cloudsave_results = true # Whether to show API call results in Developer Tools - Console
#                  You might want to turn that false in the final published version

# To add this NewGrounds.io helper script to your Godot project as an AutoLoad:
#  Save this file as ngio.gd anywhere in your project
#  In the top menu bar: Project -> Project Settings... -> AutoLoad tab
#  Click the folder button next to Path and choose this file (ngio.gd)
#  To the right of the Node Name box (it can say Ngio) click the Add button
# It should be added to your AutoLoad list and you're good to go

# Make sure you've added your App ID, and your
# AES-128/Base64 encryption key, to the lines near the top

# To make calls to the NewGrounds.io API from your scripts, use the command
#  Ngio.request(component, parameters)
# For example, to send a score to a leaderboard
#  Ngio.request("ScoreBoard.postScore", {"id": your_scoreboard_id, "value": the_score})
# Or to unlock a medal
#  Ngio.request("Medal.unlock", {"id": your_medal_id})
# For details on the components, see http://www.newgrounds.io/help/components/

# If you want to handle results from a component call, you can do it by
# making the script calling ngio.gd set up a Callable for the function that
# will handle the results, like so ...
# func _ready():
# 	Ngio.request("Medal.getList", {}, Callable(self, "handle_results"))
# 
# func handle_results(results):
# 	print ("Results: ", results) # results will show in your browser under Developer Tools in the Console tab)

# Cloud saving is more complicated and for a full description see my news post
# https://3p0ch.newgrounds.com/news/post/1294903
# But here's a quick reference
#  Ngio.cloud_save(data_to_be_saved, slot_number, Callable(self, "_on_cloudsave_complete"))
#   slot_number is optional and default is slot 1
#   the Callable is optional and in practice you probably don't want to use it
#
#  Ngio.cloud_load(Callable(self, "_on_cloud_loaded"), slot_number)
#   slot_number is optional and default is slot 1
#   the function that the Callable points to should be something like
#  func _on_cloud_loaded(loaded_data):
#   if loaded_data == null:
#    there was no data in that save slot or load attempt failed
#   elif loaded_data is TheDataStructureYouExpect: (likely a Dictionary)
#    handle the loaded data
#
#  Ngio.cloud_load_all(Callable(self, "_on_cloud_loaded"))
#   the function that Callable points to should handle each save slot as it comes in
#  func _on_cloud_loaded(slot_number, loaded_data):
#
#  To clear the data from a slot
#  Ngio.request("CloudSave.clearSlot", {"id": slot_number})

var newgroundsio_request:HTTPRequest
var gateway_uri:String = "https://newgrounds.io/gateway_v3.php"

# Ping the server occasionally to keep the session_id from timing out
var ping_cooldown_max:float = 300.0
var ping_cooldown:float = ping_cooldown_max
func _physics_process(delta):
	ping_cooldown -= delta
	if ping_cooldown < 0.0:
		request("Gateway.ping", {})
		ping_cooldown = ping_cooldown_max

func request(component, parameters, callable = null, on_load_function = null):
	if OS.has_feature('web'):
		newgroundsio_request = HTTPRequest.new()
		add_child(newgroundsio_request)
		if callable and not on_load_function:
			newgroundsio_request.request_completed.connect(_request_completed.bind(callable))
		if callable and on_load_function:
			newgroundsio_request.request_completed.connect(_request_completed.bind(callable, on_load_function))
 
		var call_parameters = {
			"component": component,
			"parameters": parameters,
		}
 
		var secure_encoding = str(JavaScriptBridge.eval(
			"""
				/*
				CryptoJS v3.1.2
				code.google.com/p/crypto-js
				(c) 2009-2013 by Jeff Mott. All rights reserved.
				code.google.com/p/crypto-js/wiki/License
				*/
				var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
				r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
				32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,
				2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},
				q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);
				a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,
				e)).finalize(b)}}});var n=d.algo={};return d}(Math);
				(function(){var u=CryptoJS,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<
				l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
				(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},
				_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),
				f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,
				m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,
				E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/
				4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
				(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,
				l)}})();
				CryptoJS.lib.Cipher||function(u){var p=CryptoJS,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},
				finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=
				c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,
				e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,
				this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,
				1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},
				decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,
				b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();
				(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,
				16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>
				8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=
				d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();
			""" +
			"var iv = CryptoJS.lib.WordArray.random(16); " +
			"var encrypted = CryptoJS.AES.encrypt('" + JSON.stringify(call_parameters) + "', CryptoJS.enc.Base64.parse('" + aes_key + "'), { iv: iv }); " +
			"CryptoJS.enc.Base64.stringify(iv.concat(encrypted.ciphertext));"
			, true))
 
		var input_parameters = {
			"app_id": app_id,
			"session_id": str(JavaScriptBridge.eval(
				'var urlParams = new URLSearchParams(window.location.search);' +
				'urlParams.get("ngio_session_id");'
				, true)),
			"call": {
				"secure": secure_encoding,
			}
		}
 
		newgroundsio_request.request(
			gateway_uri,
			["Content-Type: application/x-www-form-urlencoded"],
			HTTPClient.METHOD_POST,
			"input=" + JSON.stringify(input_parameters).uri_encode()
		)

func _request_completed(result, response_code, headers, body, callable, on_load_function = null):
	if on_load_function:
		callable.call(JSON.parse_string(body.get_string_from_ascii()), on_load_function)
	else:
		callable.call(JSON.parse_string(body.get_string_from_ascii()))


func cloud_save(data:Dictionary, slot:int = 1, callable:Callable = Callable(self, "_show_cloud_save_results")):
	var stringified_data = JSON.stringify(data)
	stringified_data = stringified_data.replace('"', '<<<doublequote>>>')
	stringified_data = stringified_data.replace("'", '<<<singlequote>>>')
	request("CloudSave.setData", {"data": stringified_data, "id": slot}, callable)

func _show_cloud_save_results(results):
	if show_cloudsave_results:
		print ("Results from cloud save request: ", results)


func cloud_load(on_load_function:Callable, slot:int = 1):
	if show_cloudsave_results:
		print("Sending cloud load request for slot ", slot)
	request("CloudSave.loadSlot", {"id": slot}, Callable(self, "_handle_single_cloud_load_results"), on_load_function)

func _handle_single_cloud_load_results(results, on_load_function:Callable):
	var load_http_request = HTTPRequest.new()
	if show_cloudsave_results:
		print("Results from single slot cloud load request (not the actual data yet): ", results)
	if not results["success"]:
		on_load_function.call(null)
		return
	if results["result"]["data"]["slot"]["url"] == null:
		on_load_function.call(null)
		return
	add_child(load_http_request)
	load_http_request.request_completed.connect(_handle_single_cloud_load_data.bind(on_load_function))
	load_http_request.request(results["result"]["data"]["slot"]["url"])

func _handle_single_cloud_load_data(result, response_code, headers, body, on_load_function:Callable):
	var stringified_data:String = body.get_string_from_ascii()
	stringified_data = stringified_data.replace('<<<doublequote>>>', '"')
	stringified_data = stringified_data.replace('<<<singlequote>>>', "'")
	if show_cloudsave_results:
		print("Loaded object: ", JSON.parse_string(stringified_data))
	on_load_function.call(JSON.parse_string(stringified_data))


func cloud_load_all(on_load_function:Callable):
	if show_cloudsave_results:
		print("Sending cloud load request for all slots")
	request("CloudSave.loadSlots", {}, Callable(self, "_handle_multiple_cloud_load_results"), on_load_function)

func _handle_multiple_cloud_load_results(results, on_load_function:Callable):
	var load_http_request
	if show_cloudsave_results:
		print("Results from all slot cloud load request (not the actual data yet): ", results)
	if not results["success"]:
		on_load_function.call(null)
		return
	for slot in results["result"]["data"]["slots"]:
		if slot["url"] == null:
			on_load_function.call(slot["id"], null)
		else:
			load_http_request = HTTPRequest.new()
			add_child(load_http_request)
			load_http_request.request_completed.connect(_handle_multiple_cloud_load_data.bind(on_load_function, slot["id"]))
			load_http_request.request(slot["url"])

func _handle_multiple_cloud_load_data(result, response_code, headers, body, on_load_function:Callable, slot_number):
	var stringified_data:String = body.get_string_from_ascii()
	stringified_data = stringified_data.replace('<<<doublequote>>>', '"')
	stringified_data = stringified_data.replace('<<<singlequote>>>', "'")
	if show_cloudsave_results:
		print("Loaded from slot #", slot_number, ": ", JSON.parse_string(stringified_data))
	on_load_function.call(slot_number, JSON.parse_string(stringified_data))

Tags:

10

Posted by 3p0ch - May 13th, 2023


Eydi released Pico's Puzzle UFO: Shooting Stars for Pico Day 2023 and it's a damn hard but good (once you get the hang of it) puzzle game that has left a lot of people stumped. A u-toob walkthrough of the regular levels has been posted, which doesn't include hidden bonus levels. After you get the gun, you can backtrack to the first level and take alternate paths to get to the bonus levels if you're up for it. And they require you to make use of a funky mechanic -- if you press R to reset a level, it doesn't reset everything on the level, just the island that you're standing on at the time. You have to make use of that mechanic for these levels.


Even getting to the bonus levels is tricky. Now that you know they exist, go try finding them and beating them, but if you get stuck then here's how I did it. (Images are here on NG, not a youtube. Green arrows are movement, red shooting / resetting.)

Getting to bonus level 1

Bonus level 1

Getting to bonus level 2

Bonus level 2

Getting to bonus level 4 (yeah I did 4 next)

Bonus level 4

Getting to bonus level 3

Bonus level 3


3

Posted by 3p0ch - May 13th, 2023


iu_970333_3205434.png

iu_970335_3205434.png

iu_970334_3205434.png

iu_970336_3205434.png

iu_970337_3205434.png


Posted by 3p0ch - May 13th, 2023


iu_970331_3205434.webp

iu_970332_3205434.webp


Posted by 3p0ch - May 13th, 2023


iu_970327_3205434.png

iu_970328_3205434.png

iu_970329_3205434.png

iu_970330_3205434.png


Posted by 3p0ch - May 13th, 2023


iu_970323_3205434.webp


iu_970325_3205434.webp

iu_970324_3205434.webp


Posted by 3p0ch - May 13th, 2023


iu_970312_3205434.png

iu_970313_3205434.png

iu_970314_3205434.png

iu_970315_3205434.png


Posted by 3p0ch - May 13th, 2023


iu_970310_3205434.webp

iu_970307_3205434.webp

iu_970308_3205434.webp

iu_970309_3205434.webp


Posted by 3p0ch - May 13th, 2023


iu_970302_3205434.png

iu_970303_3205434.png

iu_970304_3205434.png

iu_970305_3205434.png


Posted by 3p0ch - May 13th, 2023


iu_970297_3205434.webp

iu_970299_3205434.webp

iu_970298_3205434.webp

iu_970300_3205434.webp

iu_970301_3205434.webp