Downloads containing SmokeSaveLoad1.0.asc

Downloads
Name Author Game Mode Rating
Smoke Save Load Library SmokeNC Other N/A Download file

File preview

/*
Made by Smoke[NC] December 2022

Description: 
This library allows the player to save/load the game at the savepoints/checkpoints (The blue box that a bunny doll gets out from when you touch it)

When at a savepoint, the player can type 'jjsave' to save his progress
To load your progress, go to the FIRST checkpoint that the player encounters and type 'jjload', basically you want to put a checkpoint close to the start pos
Loading is allowed only at the first checkpoint at the first encounter, while saving is allowed at any checkpoint
The script does not save any of the game objects states of enemies, pickups, etc... but only the player's and the most important global states (triggers, waterlevel, etc)
The user can pass a hook function to save custom variables of his own.
Currently only 1 save slot per level, saving overwrites the previous save file, whose format is: <LevelName>_SaveGame.asdat

Usage:
	Simply call SmokeSaveLoad::Init function under onLevelLoad.
	Example:
   
		#include "SmokeSaveLoad1.0.asc"
		void onLevelLoad()
		{
			SmokeSaveLoad::Init(); 
		}
	
	you can also pass hook functions for saving and loading extra script variables
	Example:
		int banana = 0;
		int apple = 0;
		void MyCustomExtraVarsSave(jjSTREAM @stream) {stream.push(banana); stream.push(apple);}
		void MyCustomExtraVarsLoad(jjSTREAM @stream) {stream.pop(banana); stream.pop(apple);}

		void onLevelLoad()
		{
			SmokeSaveLoad::Init(@MyCustomExtraVarsSave, @MyCustomExtraVarsLoad); 
		}
*/
namespace SmokeSaveLoad
   {

	funcdef void HookSave(jjSTREAM @stream);
	funcdef void HookLoad(jjSTREAM @stream);
	
	/* SmokeSaveLoad::Init
		This is the only function that the user needs to call! Read description above.
	*/
	void Init(HookSave @userVarsSave = null, HookLoad @userVarsLoad = null)
	{
		@ hookSave = @ userVarsSave;
		@ hookLoad = @ userVarsLoad;
		 
		jjObjectPresets[OBJECT::SAVEPOST].behavior = CheckPoint();
	}
	
	/* Shared Vars */
	const float version = 1.0f;
	HookSave @hookSave = null;
	HookSave @hookLoad = null;
	bool playerWantSave = false;
	bool playerWantLoad = false;
	bool playerInCheckPoint = false;
	
	
	class BasicPlayerState
	{
		uint8 health;
		array<int> ammo(9);
		array<bool> powerup(9);
		int score;
		int lives;
		int coins;
		int food;

		array<int> gems(4);
		int fastfire;
		int8 light;
		uint8 lighting;
		
		uint charCurr;
		uint lightType;

		float xPos;
		float xOrg;
		float yPos;
		float yOrg;
		
		void GetBasicState()
		{
			jjPLAYER @ player = jjLocalPlayers[0];
			health = player.health;
			for(int iAmmo = 0; iAmmo < 9; iAmmo++)
			{
				ammo[iAmmo] = player.ammo[iAmmo];
				powerup[iAmmo] = player.powerup[iAmmo];
			}
			
			for(int iGem = 0; iGem < 4; iGem++)
			{
				gems[iGem] = player.gems[GEM::Color(iGem)];
			}
			
			fastfire = player.fastfire;
			light = player.light;
			lighting = player.lighting;
			lightType = uint(player.lightType);
			
			score = player.score;
			lives = player.lives;
			charCurr = uint(player.charCurr);
			coins = player.coins;
			food =  player.food;
			
			xPos =  player.xPos;
			yPos =  player.yPos;
			xOrg =  player.xOrg;
			yOrg =  player.yOrg;
		}
		
		void SetBasicState()
		{
			jjPLAYER @ player = jjLocalPlayers[0];
			player.health = health;
			for(int iAmmo = 0; iAmmo < 9; iAmmo++)
			{
				player.ammo[iAmmo] = ammo[iAmmo];
				player.powerup[iAmmo] = powerup[iAmmo];
			}
			
			for(int iGem = 0; iGem < 4; iGem++)
			{
				player.gems[GEM::Color(iGem)] = gems[iGem];
			}
			
			player.fastfire = fastfire;
			player.light = light;
			player.lighting = lighting;
			player.lightType = LIGHT::Type(lightType);
			
			
			player.score = score;
			player.lives = lives;
			player.setScore(score);
			player.morphTo(CHAR::Char(charCurr));
			player.coins = coins;
			player.food = food;
			player.xPos = xPos;
			player.yPos = yPos;
			player.xOrg = xOrg;
			player.yOrg = yOrg;
		}
	};
	
	class BasicGlobalState
	{
		array<bool> _jjTriggers(32);
		float _jjWaterLevel;
		string _jjMusicFileName;
		int _jjDifficulty;
		
		void GetBasicState()
		{
			for(int iTrig = 0; iTrig < 32; iTrig++)
			{
				_jjTriggers[iTrig] = jjTriggers[iTrig];
			}
			
			_jjWaterLevel = jjWaterLevel;
			_jjMusicFileName = jjMusicFileName;
			_jjDifficulty = jjDifficulty;
		}
		
		void SetBasicState()
		{
			if(_jjDifficulty != jjDifficulty)
			{
				jjAlert("ERROR! Difficulties must match! Reload the game in the same difficulty");
			}
			
			for(int iTrig = 0; iTrig < 32; iTrig++)
			{
				jjTriggers[iTrig] = _jjTriggers[iTrig];
			}

			jjSetWaterLevel(_jjWaterLevel, false);
			jjMusicLoad(_jjMusicFileName);
		}
	}
	

	
	void SaveGameState()
	{
		jjSTREAM saveState;
		BasicPlayerState playerState;
		BasicGlobalState globalState;
		playerState.GetBasicState();
		globalState.GetBasicState();
		
		saveState.push(version);

		saveState.push(playerState.health);
		
		for(int i = 0; i < 9; i++)
		{
			saveState.push(playerState.ammo[i]);
			saveState.push(playerState.powerup[i]);
		}
		for(int i = 0;i < 4; i++)
		{
			saveState.push(playerState.gems[i]);
		}

		saveState.push(playerState.fastfire);
		saveState.push(playerState.light);
		saveState.push(playerState.lighting);
		saveState.push(playerState.lightType);

			
		saveState.push(playerState.score);
		saveState.push(playerState.lives);
		saveState.push(playerState.charCurr);
		saveState.push(playerState.coins);
		saveState.push(playerState.food);
		saveState.push(playerState.xPos);
		saveState.push(playerState.yPos);
		
		saveState.push(playerState.xOrg);
		saveState.push(playerState.yOrg);
		
		for(int i = 0; i < 32; i++)
		{
			saveState.push(globalState._jjTriggers[i]);
		}
		saveState.push(globalState._jjWaterLevel);
		saveState.push(globalState._jjMusicFileName);
		saveState.push(globalState._jjDifficulty);
		
		if(hookSave !is null)
		{
			hookSave(@saveState);
		}
		
		string saveFileName = jjLevelName + "_SaveGame" + ".asdat";
		saveState.save(saveFileName);
	}
	
	
	bool LoadGameState()
	{
		string saveFileName = jjLevelName + "_SaveGame" + ".asdat";
		jjSTREAM loadState(saveFileName);
		if(loadState.isEmpty())
		{
			jjAlert("You can't load if you have no save!", true, STRING::MEDIUM);
			return false;
		}
		
		
		BasicPlayerState playerState;
		BasicGlobalState globalState;
		
		float loadedVersion;

		loadState.pop(loadedVersion);
		
		if(version != loadedVersion)
		{
			jjAlert("Version mismatch, loaded " + loadedVersion + " but script version is " + version, true, STRING::MEDIUM);
			return false;
		}

		loadState.pop(playerState.health);
		
		for(int i = 0; i < 9; i++)
		{
			loadState.pop(playerState.ammo[i]);
			loadState.pop(playerState.powerup[i]);
		}
		for(int i = 0;i < 4; i++)
		{
			loadState.pop(playerState.gems[i]);
		}
		
		loadState.pop(playerState.fastfire);
		loadState.pop(playerState.light);
		loadState.pop(playerState.lighting);
		loadState.pop(playerState.lightType);

		loadState.pop(playerState.score);
		loadState.pop(playerState.lives);
		loadState.pop(playerState.charCurr);
		loadState.pop(playerState.coins);
		loadState.pop(playerState.food);
		loadState.pop(playerState.xPos);
		loadState.pop(playerState.yPos);
		
		loadState.pop(playerState.xOrg);
		loadState.pop(playerState.yOrg);
		
				
		for(int i = 0; i < 32; i++)
		{
			loadState.pop(globalState._jjTriggers[i]);
		}
		loadState.pop(globalState._jjWaterLevel);
		loadState.pop(globalState._jjMusicFileName);
		loadState.pop(globalState._jjDifficulty);
		
		if(globalState._jjDifficulty != jjDifficulty)
		{
			jjAlert("Game vs SaveFile difficulty mismatch: " + 
			DiffToStr(jjDifficulty) + " vs " + DiffToStr(globalState._jjDifficulty), true, STRING::MEDIUM);
			jjAlert("To load your load file, you must play the game in " + DiffToStr(globalState._jjDifficulty) + " mode" , true, STRING::MEDIUM);
			return false;
		}

		globalState.SetBasicState();
		playerState.SetBasicState();
		
		if(hookLoad !is null)
		{
			hookLoad(@loadState);
		}
		return true;
	}
	
	
	class CheckPoint: jjBEHAVIORINTERFACE 
	{
		bool showText = true;
		int checkPointsEncountered = 0;

	    void onBehave(jjOBJ @ obj) 
		{
			bool enableLoad = false;
			if(obj.state == STATE::START)
			{
				checkPointsEncountered++;
			}
			
			obj.behave(BEHAVIOR::CHECKPOINT, true);
			
			int playerId = obj.findNearestPlayer((32 * 2) * (32 * 2));
			int playerId2 = obj.findNearestPlayer((32*6) * (32 * 6));
			
			if(playerId2 >= 0)
			{
				if (playerId >= 0)
				{
					if(checkPointsEncountered == 1) // first checkpoint encountered
					{
						enableLoad = true;
					}

					playerInCheckPoint = true;
					
					jjPLAYER @ player = jjPlayers[playerId];
					if(showText == true)
					{
						if(enableLoad)
						{
							player.showText("@@@@@@To save game type \"jjsave\" @To load game type \"jjload\" ",  STRING::MEDIUM);
						}
						else
						{
							player.showText("@@@@@@To save game type \"jjsave\" ",  STRING::MEDIUM);
						}
						showText = false;
					}
					
					if(playerWantSave == true)
					{
						playerWantSave = false;
						SaveGameState();
						jjAlert("Game Saved Succesfully!", true, STRING::MEDIUM);
					}
					
					if((playerWantLoad == true) && enableLoad)
					{
						playerWantLoad = false;
						if(LoadGameState() == true)
						{
							jjAlert("Game Loaded Succesfully!", true, STRING::MEDIUM);
						}
					}
				}
				else
				{
					showText = true;
					playerInCheckPoint = false;
				}
			}
		}
	}
	
	void HandlePlayerInput(string &in input)
	{
		if((input == "jjsave" || input == "jjSave"))
		{
			if(playerInCheckPoint == true)
			{
				playerWantSave = true;
			}
			else
			{
				jjAlert("You must stand on a checkpoint to save!", true, STRING::MEDIUM);
			}
		}
		if((input == "jjload" || input == "jjLoad"))
		{
			if(playerInCheckPoint == true)
			{
				playerWantLoad = true;
			}
			else
			{
				jjAlert("You must stand on the FIRST checkpoint to load!", true, STRING::MEDIUM);
			}
		}
	}
	
	string DiffToStr(int diff)
	{
        switch (diff) 
		{
			case 0: return "Easy";
			case 1: return "Normal";
			case 2: return "Hard";
			case 3: return "Turbo";
		}
		return "NULL";
	}
}

bool onCheat(string &in cheat)
{
	SmokeSaveLoad::HandlePlayerInput(cheat);
	return false;
}