Elona Wiki
Advertisement

New File Layout[]

 0: Enabled Flag
 1: Available Slots (could be removed)
 2: Action List initialized flag
 3: Learning Skill ID
 4: Learning Skill Progress
 5: Number of known actions
 6: Ignore MP Flag
 7: Temp Pref. Ranged
 8: Unused
 9: Unused
 
 10-24: Entity 1-15
 25-39: Condition 1-15
 40-54: Comparator 1-15
 55-69: Value 1-15
 70-84: Action 1-15
 85-104: Known Action IDs
 105-124: Fatigue for known action IDs
  


Elona Save Format[]

The .s1 files contained as part of Elona saves are gzip'd binary files. This is possibly (probably) true for the other files as well, but I haven't investigated them.

  • gdata.s1: This file is simply a list of 32-bit signed integer values stored in little-endian format. In the 1.43 source, data from this file is referenced under var_64(x), where x corresponds to the number of the entry, located at offset x*4.

To add a file to Elona's save (Integer Based):[]

  • Define the variable it will be stored in, and give it dimensions. Dim instruction is used to set array size. Example:
    • dim TestData, 115, 15
    • This creates a 2D array named TestData that is 15 sets of 115 values. Good place to put this is just after the line containing "dim var_57".
  • Find the line containing "ctrlFile:game". This is the beginning of a loop that loads static save data. Increment the loop counter (e.g. from the default "repeat 15" to "repeat 16").
  • Under the last "If ( cnt == x )" section, add a new section, with cnt being equal to the max loop counter - 1. Example section:
if ( cnt == 15 ) {
	var_428 += "testdata.s1"
	var_1960 = 3680
}
  • var_428 (or whatever the variable is in the latest decompilation - don't assume this variable name is current) already holds the save folder data. This just appends the file name to it.
  • var_1960 is the size, in bytes, of the file. For one dimensional arrays, this is just size * 4. For two-dimensional arrays, this is sizeX * sizeY + 17 * sizeX
  • Now we need to tell the game to actually save/load these values. First find the last zClose before the loop counter and add a section like this:
   if ( cnt == 15 ) {
   	if ( var_856 == 0 ) {
   		zWrite TestData, var_1955, var_1960
   	}
   	if ( var_856 == 1 ) {
   		zRead TestData, var_1955, var_1960
   	}
   }
   
  • This tells the game to load/save this file when saving the game (and creation upon a new game).
  • (Optional) In the section that looks like this: "if ( cnt != 14 | var_64(96) >= 1200 ) {", modify the check against the cnt variable to something like "cnt < 14". This will allow the game to create a new, empty file for characters that have already been generated Otherwise, the game will crash.

Menu Creation (Choice Based)[]

"Choice Based" menus are the menus that display no text above them - like the (i)nteraction menu, the exit menu, and the living weapon menu.

There are five important parts to creating this type of menu.

  • var_1095 - Holds the number of available choices. Can be set beforehand if the number of choices is static, or incremented if choices are dynamic.
  • var_426(0, choice#) - This holds the string to display, the shortcut key, and the return value (as a string)
  • var_371 - holds the game window x/y values, the x value of the menu in pixels (y value is calculated), and the "style".
  • label_2452 - handles drawing the window and handles execution while the window is open.
  • var_383 - holds the return value of the option that was chosen.

So, the code for an example window might look like this:

   *TestMenu
   var_426(0, 0) = "Hello, World!", "a", "0"
   var_426(0, 1) = "Testing 123!", "b", "1"
   var_1095 = 2
   
   var_371 = var_1096, var_1097, 600, 1
   gosub *label_2452
   
   if (var_383 == 0){
    mes "Hello to you too!"
   } else {
    mes "Test successful!"
   }
   
   return
   

Elosnack Values[]

Multiply these by 4 to get the elosnack offset.

g.data:

  • 8 = ?
  • 9 = ? Possibly unused. Only referenced twice, and value never checked.
  • 13 = hour?
  • 14 = minute?
  • 15 = related to quest timer somehow
  • 16 = something related to containers? Total number of accessible container spaces? Increases by one if you get a "new" cooler and sets some property of the cooler to it. Do separate coolers access separate spaces?
  • 18 = Time left on current weather effect
  • 19 = previous map? used in the "You left <x>." messages.
  • 545 = Fron can spawn (0), Fron can't spawn (1). Confirmed.
  • 84 = quest timer? (min left for quest)

De-obfuscated Functions[]

  • Function dmghp (WIP - 1.43)
dmghp(Target, RawDamage, Attacker, AttackElement = 0, RestoreValue = 0)
{
If(Target.IsAlive() == False)
	return;
	
CalculatedDamage = RawDamage * (1 + Target.HasStatus(Berserk));

If(Attacker.Exists())
	If(Attacker.HasStatus(Browbeat))
		CalculatedDamage = CalculatedDamage/2;

If (AttackElement.Exists() && AttackElement.Type != Physical)
	{
		TargetResistLevel = Target.ResistVal(AttackElement) / 50;
		if (TargetResistLevel < 3)
			CalculatedDamage = CalculatedDamage * 150 / limit(TargetResistLevel * 50 + 50, 40, 150);
		else
			CalculatedDamage = CalculatedDamage * 100 / (TargetResistLevel * 50 + 50);

		CalculatedDamage = CalculatedDamage * 100 / (Target.ResistVal(Magic) / 2 + 50);	
	}
...
  • Label_2533 (Shop Item Generation - Partially decoded, with notes)
Label_2533
	
/* Shop ID's
	1001 - Armory
	1006 - General Store
	1004 - Magic Store
	1008 - Goods Store
	1007 - Blackmarket
	1018 - Souvenir
	1017 - Dye
	1010 - Wandering Vendor
	2003 - house visiting merchant?
	1016 - Garolt/Mirok. Whichever one it is that sells stuff.
	1023 - Arcbelc, music ticket vendor?
	1024 - Unknown. One in Melkawn. Monster Ball vendor?
	1019 - Sister merchant in mansion of little sister
	1020 - Spell Writer (Mage's Guild)
	1015 - Moyer
	1003 - Bakery
	1002 - Food (includes Aile)
*/
	
/* Item Categories
	0 	 Base
	10000 	 Weapon
	12000 	 Helm
	14000 	 Shield
	16000 	 Armor
	18000 	 Boots
	19000 	 Girlde
	20000 	 Cloak
	22000 	 Glove
	24000 	 Range
	25000 	 Ammo
	32000 	 Ring
	34000 	 Amulet
	53000 	 Scroll
	54000 	 Spellbook
	55000 	 Book
	56000 	 Staff
	57000 	 Food
	59000 	 Tool
	60000 	 Furniture
	60001 	 Well
	64000 	 Junk
	77000 	 Ore
	80000 	 Tree
	92000 	 Trade
*/	

/* Other Details
	var_574 is used in the item create function. Overrides var_573.
*/

	//Presumably the following code clears the inventory of the vendor
	inv_getheader -1
	repeat var_630, var_629
		var_99(0, cnt) = 0
	loop
	
	//Temporary name until I know what this does
	ShopFactor = 20 + Vendor.ShopRank() / 2

	if ( ShopFactor > 80 ) {
		ShopFactor = 80
	}
	if ( Vendor.ShopID() == "blackmarket" ) {
		ShopFactor = 5 + limit(Vendor.ShopRank() / 10, 1, 20)
	}
	if ( Vendor.ShopID() == "armory" ) {
		ShopFactor = 10 + limit(Vendor.ShopRank() / 10, 1, 90)
	}
	if ( Vendor.ShopID() == "wandering vendor" ) {
		ShopFactor = 4 + rnd(4)
	}
	if ( Vendor.ShopID() == 2003 ) {
		ShopFactor = 4 + rnd(4)
	}
	if ( Vendor.ShopID() == "Garok" ) {
		ShopFactor = 20
	}
	if ( Vendor.ShopID() == "Souvenir" ) {
		ShopFactor /= 2
	}
	if ( Vendor.ShopID() == "Music Ticket" ) {
		ShopFactor = 15
	}
	if ( Vendor.ShopID() == 1024 ) {
		ShopFactor /= 4
	}
	
	//repeat the following loop ShopFactor times
	repeat var_354
		
		var_59 = 0		
		
		if ( Vendor.ShopID() == "Spell Writer" ) {
			var_635 = 0
			
			// Loops 999 times.
			repeat 999
				if ( var_434(2, cnt) > 1 ) {
					var_421(0, var_635) = cnt
					var_635++
				}
			loop
			//End loop
			
			if ( var_635 == 0 ) {
				return
			}
			var_59 = var_421(0, rnd(var_635))
		}
		
		if ( Vendor.ShopID() == "general store" ) {
			var_354 = rnd(5)
			if ( var_354 == 0 ) {
				var_573 = 25000 //Ammo
			}
			if ( var_354 == 1 ) {
				var_573 = 60000 //Furniture
			}
			if ( var_354 == 2 ) {
				var_573 = 20000 //Cloaks
			}
			if ( var_354 == 3 ) {
				var_573 = 77000 //Ore
			}
			if ( var_354 == 4 ) {
				var_573 = 59000 //Tools
			}
			if ( rnd(20) == 0 ) {
				var_59 = 734 //Gamble Chest
			}
			if ( rnd(8) == 0 ) {
				var_573 = 91000 //Cargo Rations
			}
			if ( rnd(10) == 0 ) {
				var_59 = var_1315(rnd(length(var_1315)))
			}
			if ( var_64(11) == 2 ) {
				if ( rnd(2) == 0 ) {
					var_59 = 907 //Chocolate Kit
				}
			}
		}
		if ( Vendor.ShopID() == "Bakery" ) {
			if ( rnd(3) != 0 ) {
				var_574 = 57001 //Flour Products
			}
			else {
				var_574 = 57002 //Noodle Products
			}
		}
		if ( Vendor.ShopID() == "Food" ) {
			if ( var_57(27, var_243) != 534 ) { // If the food vendor is not Aile
				if ( rnd(3) != 0 ) {
					continue
				}
			}
			if ( var_57(27, var_243) == 534 ) { // If the food vendor is Aile
				if ( rnd(5) != 0 ) {
					continue
				}
			}
			var_573 = 57000 //Food
			if ( rnd(5) == 0 ) {
				var_573 = 91000 // Cargo Rations
			}
			if ( var_57(27, var_243) == 534 ) { // If Aile
				if ( rnd(5) == 0 ) {
					var_59 = 910 //Miniature Blimp
				}
				if ( rnd(10) == 0 ) {
					var_59 = 520 //Wing
				}
			}
		}
		if ( Vendor.ShopID() == 1010 | Vendor.ShopID() == 2003 ) {
			var_573 = var_580(rnd(length(var_580)))
			var_576 = 3
			if ( rnd(2) == 0 ) {
				var_576 = 4
			}
		}
		if ( Vendor.ShopID() == 1005 ) {
			var_573 = 91000
			if ( rnd(4) ) {
				var_574 = 52002
			}
			if ( rnd(20) == 0 ) {
				var_59 = 734
			}
		}
		if ( Vendor.ShopID() == "goods store" ) {
			var_573 = 56000
			if ( rnd(3) == 0 ) {
				var_573 = var_580(rnd(length(var_580)))
			}
			if ( rnd(3) == 0 ) {
				var_573 = 60000
			}
			if ( rnd(5) == 0 ) {
				var_573 = 57000
			}
			if ( rnd(4) == 0 ) {
				var_573 = 53000
			}
			if ( rnd(15) == 0 ) {
				var_573 = 55000
			}
			if ( rnd(10) == 0 ) {
				var_573 = 91000
			}
			if ( rnd(10) == 0 ) {
				var_59 = var_1315(rnd(length(var_1315)))
			}
			if ( rnd(15) == 0 ) {
				var_59 = 511
			}
		}
		if ( Vendor.ShopID() == "armory" ) {
			if ( rnd(limit(Vendor.ShopRank(), 1, 1000) + 200) > rnd(1000) ) {
				var_576 = 3
			}
			var_354 = rnd(6)
			if ( var_354 == 0 ) {
				var_573 = 16000
			}
			if ( var_354 == 1 ) {
				var_573 = 12000
			}
			if ( var_354 == 2 ) {
				var_573 = 22000
			}
			if ( var_354 == 3 ) {
				var_573 = 18000
			}
			if ( var_354 == 4 ) {
				var_573 = 14000
			}
			if ( var_354 == 5 ) {
				var_573 = 19000
			}
			if ( rnd(3) == 0 ) {
				if ( rnd(3) == 0 ) {
					var_573 = 10000
				}
				else {
					var_573 = 24000
				}
			}
		}
		if ( Vendor.ShopID() == 1009 ) {
			var_573 = 92000
		}
		if ( Vendor.ShopID() == 1021 ) {
			var_573 = 59000
			if ( rnd(2) == 0 ) {
				var_59 = 636
			}
			if ( rnd(2) == 0 ) {
				var_59 = 629
			}
		}
		if ( Vendor.ShopID() == 1011 ) {
			if ( rnd(4) == 0 ) {
				var_573 = 24000
			}
			if ( rnd(5) == 0 ) {
				var_573 = 25000
			}
			if ( rnd(3) == 0 ) {
				var_573 = 57000
			}
			fltn "sf"
		}
		if ( Vendor.ShopID() == 1013 ) {
			if ( rnd(3) != 0 ) {
				continue
			}
			var_573 = 55000
			if ( rnd(3) ) {
				var_59 = var_1315(rnd(length(var_1315)))
			}
			if ( rnd(5) == 0 ) {
				var_59 = 511
			}
			if ( rnd(5) == 0 ) {
				var_59 = 897
			}
			if ( rnd(20) == 0 ) {
				var_59 = 712
			}
		}
		if ( Vendor.ShopID() == "Souvenir" ) {
			fltn "spshop"
			if ( rnd(20) == 0 ) {
				var_59 = 869
			}
		}
		if ( Vendor.ShopID() == 1022 ) {
			fltn "fest"
			if ( rnd(12) == 0 ) {
				var_59 = 750
			}
			if ( rnd(12) == 0 ) {
				var_59 = 751
			}
			if ( rnd(5) == 0 ) {
				var_59 = 770
			}
			if ( rnd(5) == 0 ) {
				var_59 = 867
			}
			if ( var_64(20) == 33 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 762
				}
			}
			if ( var_64(20) == 33 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 768
				}
			}
			if ( var_64(20) == 33 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 769
				}
			}
			if ( var_64(20) == 33 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 779
				}
			}
			if ( var_64(20) == 33 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 780
				}
			}
			if ( var_64(20) == 57 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 759
				}
			}
			if ( var_64(20) == 57 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 512
				}
			}
			if ( var_64(20) == 57 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 873
				}
			}
			if ( var_64(20) == 57 ) {
				if ( rnd(12) == 0 ) {
					var_59 = 872
				}
			}
			if ( var_64(20) == 57 ) {
				if ( rnd(5) == 0 ) {
					var_59 = 844
				}
			}
		}
		if ( Vendor.ShopID() == 1024 ) {
			var_59 = 685
		}
		if ( Vendor.ShopID() == "Dye" ) {
			var_59 = 519
		}
		if ( Vendor.ShopID() == 1012 ) {
			var_573 = 60000
			if ( cnt == 0 ) {
				var_59 = 510
			}
			if ( cnt == 1 ) {
				var_59 = 561
			}
			if ( cnt == 2 ) {
				var_59 = 562
			}
			if ( cnt == 3 ) {
				var_59 = 547
			}
			if ( cnt == 4 ) {
				var_59 = 579
			}
			if ( cnt == 5 ) {
				var_59 = 576
			}
			if ( cnt == 6 ) {
				var_59 = 611
			}
			if ( cnt == 7 ) {
				var_59 = 701
			}
			if ( cnt > 10 ) {
				if ( rnd(3) != 0 ) {
					continue
				}
			}
			if ( cnt == 19 ) {
				var_59 = 413
			}
			if ( cnt == 20 ) {
				var_59 = 414
			}
			if ( cnt == 21 ) {
				var_59 = 616
			}
		}
		if ( Vendor.ShopID() == 1014 ) {
			var_59 = 617
		}
		if ( Vendor.ShopID() == 1016 ) {
			if ( cnt == 0 ) {
				var_59 = 430
			}
			if ( cnt == 1 ) {
				var_59 = 431
			}
			if ( cnt == 2 ) {
				var_59 = 502
			}
			if ( cnt == 3 ) {
				var_59 = 480
			}
			if ( cnt == 4 ) {
				var_59 = 421
			}
			if ( cnt == 5 ) {
				var_59 = 603
			}
			if ( cnt == 6 ) {
				var_59 = 615
			}
			if ( cnt == 7 ) {
				var_59 = 559
			}
			if ( cnt == 8 ) {
				var_59 = 516
			}
			if ( cnt == 9 ) {
				var_59 = 616
			}
			if ( cnt == 10 ) {
				var_59 = 623
			}
			if ( cnt == 11 ) {
				var_59 = 505
			}
			if ( cnt == 12 ) {
				var_59 = 624
			}
			if ( cnt == 13 ) {
				var_59 = 625
			}
			if ( cnt == 14 ) {
				var_59 = 626
			}
			if ( cnt == 15 ) {
				var_59 = 627
			}
			if ( cnt == 16 ) {
				var_59 = 56
			}
			if ( cnt == 17 ) {
				var_59 = 742
			}
			if ( cnt == 18 ) {
				var_59 = 760
			}
			if ( cnt == 19 ) {
				var_59 = 892
			}
			if ( cnt > 19 ) {
				continue
			}
		}
		if ( Vendor.ShopID() == 1023 ) {
			if ( cnt == 0 ) {
				var_59 = 799
			}
			if ( cnt == 1 ) {
				var_59 = 427
			}
			if ( cnt == 2 ) {
				var_59 = 425
			}
			if ( cnt == 3 ) {
				var_59 = 426
			}
			if ( cnt == 4 ) {
				var_59 = 424
			}
			if ( cnt == 5 ) {
				var_59 = 423
			}
			if ( cnt == 6 ) {
				var_59 = 422
			}
			if ( cnt == 7 ) {
				var_59 = 809
			}
			if ( cnt == 8 ) {
				var_59 = 811
			}
			if ( cnt == 9 ) {
				var_59 = 847
			}
			if ( cnt == 10 ) {
				var_59 = 816
			}
			if ( cnt == 11 ) {
				var_59 = 655
			}
			if ( cnt == 12 ) {
				var_59 = 662
			}
			if ( cnt == 13 ) {
				var_59 = 639
			}
			if ( cnt > 13 ) {
				continue
			}
		}
		itemcreate -1, var_59, -1, -1, 0
		if ( stat == 0 ) {
			break
		}
		if ( Vendor.ShopID() == 1016 ) {
			var_99(0, var_514) = 1
			var_99(17, var_514) = 0
			if ( var_99(3, var_514) == 480 ) {
				var_99(9, var_514) = 4
			}
			continue
		}
		if ( Vendor.ShopID() == 1023 ) {
			var_99(0, var_514) = 1
			var_99(17, var_514) = 0
			continue
		}
		var_1246 = 0
		if ( instr(var_476(var_99(3, var_514)), 0, "/neg/") != (-1) ) {
			var_1246 = 1
		}
		if ( instr(var_476(var_99(3, var_514)), 0, "/noshop/") != (-1) ) {
			if ( Vendor.ShopID() != Souvenir ) {
				var_1246 = 1
			}
		}
		if ( var_1246 ) {
			var_99(0, var_514) = 0
			continue
		}
		if ( Vendor.ShopID() == 1012 ) {
			var_573 = 60000
		}
		gosub *label_2534
		var_99(0, var_514) = rnd(var_381) + 1
		if ( Vendor.ShopID() == 1003 ) {
			if ( rnd(5) <= 2 ) {
				var_99(0, var_514) = limit(rnd(Vendor.ShopRank() + 1) / 30 + 1, 1, 3)
			}
		}
		if ( Vendor.ShopID() == 1009 ) {
			var_354 = var_483(var_99(25, var_514))
			if ( var_354 <= 70 ) {
				var_99(0, var_514) = var_99(0, var_514) * 200 / 100
			}
			if ( var_354 <= 50 ) {
				var_99(0, var_514) = var_99(0, var_514) * 200 / 100
			}
			if ( var_354 >= 80 ) {
				var_99(0, var_514) = var_99(0, var_514) / 2 + 1
				if ( rnd(2) ) {
					var_99(0, var_514) = 0
					continue
				}
			}
			if ( var_354 >= 100 ) {
				var_99(0, var_514) = var_99(0, var_514) / 2 + 1
				if ( rnd(3) ) {
					var_99(0, var_514) = 0
					continue
				}
			}
			var_99(0, var_514) = var_99(0, var_514) * (100 + Vendor.ShopRank()) / 100 + 1
		}
		var_354 = refitem(var_99(3, var_514), 5)
		if ( var_99(17, var_514) == (-1) | var_99(17, var_514) == (-2) ) {
			var_99(0, var_514) = 0
			continue
		}
		if ( var_99(17, var_514) == 1 ) {
			var_99(0, var_514) = 1
		}
		if ( var_354 == 52000 ) {
			if ( var_99(3, var_514) == 516 ) {
				var_99(0, var_514) = 0
			}
		}
		if ( var_354 == 57000 ) {
			if ( refitem(var_99(3, var_514), 9) == 58500 ) {
				if ( rnd(5) ) {
					var_99(0, var_514) = 0
				}
			}
		}
		if ( Vendor.ShopID() == Souvenir ) {
			var_99(1, var_514) = limit(var_99(1, var_514), 1, 1000000) * 50
			if ( var_99(3, var_514) == 729 ) {
				var_99(1, var_514) *= 10
			}
		}
		if ( Vendor.ShopID() == 1015 ) {
			var_99(1, var_514) *= 2
		}
		if ( Vendor.ShopID() == 1007 ) {
			if ( var_64(250 + 15) != 0 ) {
				var_99(1, var_514) *= 2
			}
			else {
				var_99(1, var_514) *= 3
			}
		}
		if ( Vendor.ShopID() == 1010 ) {
			var_99(1, var_514) *= 2
		}
		if ( Vendor.ShopID() == 2003 ) {
			var_99(1, var_514) = var_99(1, var_514) * 4 / 5
		}
		if ( Vendor.ShopID() == 1020 ) {
			var_99(1, var_514) = var_99(1, var_514) * 3 / 2
			if ( var_64(20) == 72 ) {
				var_99(1, var_514) = var_99(1, var_514) * 3
			}
		}
		if ( Vendor.ShopID() == 1024 ) {
			var_99(1, var_514) *= 2
		}
	loop
	//end loop by ShopFactor
	
	var_57(154, var_243) = var_64(13) + var_64(12) * 24 + var_64(11) * 24 * 30 + var_64(10) * 24 * 30 * 12 + 48 * (1 + (Vendor.ShopID() == 1009))
	if ( Vendor.ShopID() == 1009 ) {
		var_57(154, var_243) = var_64(13) + var_64(12) * 24 + var_64(11) * 24 * 30 + var_64(10) * 24 * 30 * 12 + 168
	}
	return
Advertisement