Save character with YAJL

This commit is contained in:
Cong
2025-02-16 22:53:37 +11:00
parent 7639481bd9
commit 46b293ebe9
7 changed files with 650 additions and 433 deletions

View File

@@ -4,8 +4,14 @@
"Author": "Cong",
"Description": "The treacherous Clan Tantalus are amassing an army of super solders. You are an elite Kardausar soldier of the Emperor; travel to the harsh planet of Sand, the Tantalus fief, and carry out His will.",
"Ammo": false,
"WeaponPersist": false,
"SkipWeaponMenu": false,
"BuyAndSell": false,
"RandomPickups": true,
"DoorOpenTicks": 70,
"Lives": 0,
"MaxLives": 0,
"PlayerHP": 0,
"PlayerMaxHP": 0,
"PlayerExcessHP": 0,
"Missions": 10
}

View File

@@ -1,327 +1,403 @@
{
"Version": 13,
"Characters": [{
"Class": "Ogre",
"HairType": "mohawk",
"Skin": "9c589cff",
"Arms": "9c589cff",
"Body": "9c5858ff",
"Legs": "9c5858ff",
"Hair": "9c0000ff",
"Feet": "9c5858ff",
"speed": 384,
"Gun": "Monster Bite",
"maxHealth": 40,
"flags": 0,
"probabilityToMove": 70,
"probabilityToTrack": 30,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Ogre",
"Skin": "d25106ff",
"Arms": "ff8136ff",
"Body": "ff8136ff",
"Legs": "ff8136ff",
"Hair": "c00000ff",
"Feet": "ff8136ff",
"speed": 320,
"Gun": "Monster Bite",
"maxHealth": 20,
"flags": 0,
"probabilityToMove": 60,
"probabilityToTrack": 25,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "flattop",
"Skin": "ff8136ff",
"Arms": "b79536ff",
"Body": "b79536ff",
"Legs": "b79536ff",
"Hair": "882800ff",
"Feet": "b79536ff",
"speed": 256,
"Gun": "Machine gun",
"maxHealth": 20,
"flags": 0,
"probabilityToMove": 25,
"probabilityToTrack": 15,
"probabilityToShoot": 27,
"actionDelay": 15
},
{
"Class": "Soldier",
"HairType": "peak_cap",
"Skin": "ff8136ff",
"Arms": "b79536ff",
"Body": "b79536ff",
"Legs": "b79536ff",
"Hair": "b98c00ff",
"Feet": "b79536ff",
"speed": 256,
"Gun": "Machine gun",
"maxHealth": 30,
"flags": 0,
"probabilityToMove": 20,
"probabilityToTrack": 15,
"probabilityToShoot": 37,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "eye_patch",
"Skin": "ff8136ff",
"Arms": "b79536ff",
"Body": "b79536ff",
"Legs": "b79536ff",
"Hair": "000000ff",
"Feet": "b79536ff",
"speed": 320,
"Gun": "Khanjali",
"maxHealth": 30,
"flags": 0,
"probabilityToMove": 50,
"probabilityToTrack": 15,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Cyborg",
"PlayerTemplateName": "Kardausar Colonel",
"HairType": "cyborg",
"Skin": "e29b31ff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "052020ff",
"Feet": "363636ff",
"speed": 320,
"Gun": "Lasergun",
"maxHealth": 40,
"flags": 131072,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 27,
"actionDelay": 15
},
{
"Class": "Jones",
"PlayerTemplateName": "Kardausar Private",
"HairType": "hazmat",
"Skin": "3f3f3fff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "38ff35ff",
"Feet": "363636ff",
"speed": 192,
"Gun": "Machine gun",
"maxHealth": 20,
"flags": 131072,
"probabilityToMove": 50,
"probabilityToTrack": 20,
"probabilityToShoot": 32,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "flattop",
"Skin": "ff8136ff",
"Arms": "b79536ff",
"Body": "9c9c9cff",
"Legs": "b79536ff",
"Hair": "ff6c00ff",
"Feet": "b79536ff",
"speed": 448,
"Gun": "Projector",
"maxHealth": 180,
"flags": 0,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 37,
"actionDelay": 3
},
{
"Class": "Jones",
"HairType": "beard",
"Skin": "ffffffff",
"Arms": "9c9c9cff",
"Body": "9c9c9cff",
"Legs": "9c9c9cff",
"Hair": "27282aff",
"Feet": "9c9c9cff",
"speed": 320,
"Gun": "Wurmtooth",
"maxHealth": 30,
"flags": 2048,
"probabilityToMove": 50,
"probabilityToTrack": 15,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Cyborg",
"Skin": "882800ff",
"Arms": "9c9c9cff",
"Body": "9c9c9cff",
"Legs": "9c9c9cff",
"Hair": "4b6affff",
"Feet": "9c9c9cff",
"speed": 320,
"Gun": "Wurmtooth",
"maxHealth": 30,
"flags": 0,
"probabilityToMove": 50,
"probabilityToTrack": 15,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Cyborg",
"Skin": "d25106ff",
"Arms": "9c9c9cff",
"Body": "9c0000ff",
"Legs": "9c9c9cff",
"Hair": "3ba3ffff",
"Feet": "9c9c9cff",
"speed": 320,
"Gun": "Projector",
"maxHealth": 40,
"flags": 0,
"probabilityToMove": 30,
"probabilityToTrack": 15,
"probabilityToShoot": 7,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "beard",
"Skin": "ffffffff",
"Arms": "9c9c9cff",
"Body": "9c0000ff",
"Legs": "9c9c9cff",
"Hair": "141415ff",
"Feet": "9c9c9cff",
"speed": 320,
"Gun": "Projector",
"maxHealth": 40,
"flags": 2048,
"probabilityToMove": 30,
"probabilityToTrack": 20,
"probabilityToShoot": 7,
"actionDelay": 15
},
{
"Class": "Cyborg",
"Skin": "80653cff",
"Arms": "9c9c9cff",
"Body": "58589cff",
"Legs": "9c9c9cff",
"Hair": "5f76ffff",
"Feet": "9c9c9cff",
"speed": 384,
"Gun": "Lasergun",
"maxHealth": 50,
"flags": 0,
"probabilityToMove": 30,
"probabilityToTrack": 15,
"probabilityToShoot": 17,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "donut",
"Skin": "d25106ff",
"Arms": "9c9c9cff",
"Body": "9c9c9cff",
"Legs": "9c9c9cff",
"Hair": "c6c6c6ff",
"Feet": "9c9c9cff",
"speed": 448,
"Gun": "Projector",
"maxHealth": 280,
"flags": 0,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 32,
"actionDelay": 5
},
{
"Class": "Cyborg",
"HairType": "cyborg",
"Skin": "9c6d13ff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "001500ff",
"Feet": "363636ff",
"speed": 320,
"Gun": "Inkvine",
"maxHealth": 50,
"flags": 1966080,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 25,
"actionDelay": 8
},
{
"Class": "Cyborg",
"HairType": "hazmat",
"Skin": "2e2e2eff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "00ff00ff",
"Feet": "363636ff",
"speed": 320,
"Gun": "Inkvine",
"maxHealth": 50,
"flags": 655360,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 25,
"actionDelay": 8
},
{
"Class": "Jones",
"PlayerTemplateName": "Kardausar Captain",
"HairType": "hazmat",
"Skin": "9c6513ff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "0ecd00ff",
"Feet": "363636ff",
"speed": 192,
"Gun": "Machine gun",
"maxHealth": 20,
"flags": 131072,
"probabilityToMove": 50,
"probabilityToTrack": 20,
"probabilityToShoot": 32,
"actionDelay": 15
},
{
"Class": "Jones",
"PlayerTemplateName": "Kardausar Bator",
"HairType": "eye_patch",
"Skin": "149c14ff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "100f0fff",
"Feet": "363636ff",
"speed": 192,
"Gun": "Machine gun",
"maxHealth": 20,
"flags": 131072,
"probabilityToMove": 50,
"probabilityToTrack": 20,
"probabilityToShoot": 32,
"actionDelay": 15
}]
}
"Version": 14,
"Characters": [
{
"Class": "Ogre",
"HairType": "mohawk",
"Skin": "9c589cff",
"Arms": "9c589cff",
"Body": "9c5858ff",
"Legs": "9c5858ff",
"Hair": "9c0000ff",
"Feet": "9c5858ff",
"Facehair": "9c0000ff",
"Hat": "9c0000ff",
"Glasses": "9c0000ff",
"speed": 384,
"Gun": "Monster Bite",
"maxHealth": 40,
"excessHealth": 80,
"flags": 0,
"probabilityToMove": 70,
"probabilityToTrack": 30,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Ogre",
"Skin": "d25106ff",
"Arms": "ff8136ff",
"Body": "ff8136ff",
"Legs": "ff8136ff",
"Hair": "c00000ff",
"Feet": "ff8136ff",
"Facehair": "c00000ff",
"Hat": "c00000ff",
"Glasses": "c00000ff",
"speed": 320,
"Gun": "Monster Bite",
"maxHealth": 20,
"excessHealth": 40,
"flags": 0,
"probabilityToMove": 60,
"probabilityToTrack": 25,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "flattop",
"Skin": "ff8136ff",
"Arms": "b79536ff",
"Body": "b79536ff",
"Legs": "b79536ff",
"Hair": "882800ff",
"Feet": "b79536ff",
"Facehair": "882800ff",
"Hat": "882800ff",
"Glasses": "882800ff",
"speed": 256,
"Gun": "Machine gun",
"maxHealth": 20,
"excessHealth": 40,
"flags": 0,
"probabilityToMove": 25,
"probabilityToTrack": 15,
"probabilityToShoot": 27,
"actionDelay": 15
},
{
"Class": "Soldier",
"HatType": "peak_cap",
"Skin": "ff8136ff",
"Arms": "b79536ff",
"Body": "b79536ff",
"Legs": "b79536ff",
"Hair": "b98c00ff",
"Feet": "b79536ff",
"Facehair": "b98c00ff",
"Hat": "b98c00ff",
"Glasses": "b98c00ff",
"speed": 256,
"Gun": "Machine gun",
"maxHealth": 30,
"excessHealth": 60,
"flags": 0,
"probabilityToMove": 20,
"probabilityToTrack": 15,
"probabilityToShoot": 37,
"actionDelay": 15
},
{
"Class": "Jones",
"GlassesType": "eye_patch",
"Skin": "ff8136ff",
"Arms": "b79536ff",
"Body": "b79536ff",
"Legs": "b79536ff",
"Hair": "000000ff",
"Feet": "b79536ff",
"Facehair": "000000ff",
"Hat": "000000ff",
"Glasses": "000000ff",
"speed": 320,
"Gun": "Khanjali",
"maxHealth": 30,
"excessHealth": 60,
"flags": 0,
"probabilityToMove": 50,
"probabilityToTrack": 15,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Cyborg",
"PlayerTemplateName": "Kardausar Colonel",
"GlassesType": "cyborg",
"Skin": "e29b31ff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "052020ff",
"Feet": "363636ff",
"Facehair": "052020ff",
"Hat": "052020ff",
"Glasses": "052020ff",
"speed": 320,
"Gun": "Lasergun",
"maxHealth": 40,
"excessHealth": 80,
"flags": 131072,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 27,
"actionDelay": 15
},
{
"Class": "Jones",
"PlayerTemplateName": "Kardausar Private",
"HatType": "hazmat",
"Skin": "3f3f3fff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "38ff35ff",
"Feet": "363636ff",
"Facehair": "38ff35ff",
"Hat": "38ff35ff",
"Glasses": "38ff35ff",
"speed": 192,
"Gun": "Machine gun",
"maxHealth": 20,
"excessHealth": 40,
"flags": 131072,
"probabilityToMove": 50,
"probabilityToTrack": 20,
"probabilityToShoot": 32,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "flattop",
"Skin": "ff8136ff",
"Arms": "b79536ff",
"Body": "9c9c9cff",
"Legs": "b79536ff",
"Hair": "ff6c00ff",
"Feet": "b79536ff",
"Facehair": "ff6c00ff",
"Hat": "ff6c00ff",
"Glasses": "ff6c00ff",
"speed": 448,
"Gun": "Projector",
"maxHealth": 180,
"excessHealth": 360,
"flags": 0,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 37,
"actionDelay": 3
},
{
"Class": "Jones",
"HairType": "flattop",
"FacehairType": "beard",
"Skin": "ffffffff",
"Arms": "9c9c9cff",
"Body": "9c9c9cff",
"Legs": "9c9c9cff",
"Hair": "27282aff",
"Feet": "9c9c9cff",
"Facehair": "27282aff",
"Hat": "27282aff",
"Glasses": "27282aff",
"speed": 320,
"Gun": "Wurmtooth",
"maxHealth": 30,
"excessHealth": 60,
"flags": 2048,
"probabilityToMove": 50,
"probabilityToTrack": 15,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Cyborg",
"Skin": "882800ff",
"Arms": "9c9c9cff",
"Body": "9c9c9cff",
"Legs": "9c9c9cff",
"Hair": "4b6affff",
"Feet": "9c9c9cff",
"Facehair": "4b6affff",
"Hat": "4b6affff",
"Glasses": "4b6affff",
"speed": 320,
"Gun": "Wurmtooth",
"maxHealth": 30,
"excessHealth": 60,
"flags": 0,
"probabilityToMove": 50,
"probabilityToTrack": 15,
"probabilityToShoot": 0,
"actionDelay": 15
},
{
"Class": "Cyborg",
"Skin": "d25106ff",
"Arms": "9c9c9cff",
"Body": "9c0000ff",
"Legs": "9c9c9cff",
"Hair": "3ba3ffff",
"Feet": "9c9c9cff",
"Facehair": "3ba3ffff",
"Hat": "3ba3ffff",
"Glasses": "3ba3ffff",
"speed": 320,
"Gun": "Projector",
"maxHealth": 40,
"excessHealth": 80,
"flags": 0,
"probabilityToMove": 30,
"probabilityToTrack": 15,
"probabilityToShoot": 7,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "flattop",
"FacehairType": "beard",
"Skin": "ffffffff",
"Arms": "9c9c9cff",
"Body": "9c0000ff",
"Legs": "9c9c9cff",
"Hair": "141415ff",
"Feet": "9c9c9cff",
"Facehair": "141415ff",
"Hat": "141415ff",
"Glasses": "141415ff",
"speed": 320,
"Gun": "Projector",
"maxHealth": 40,
"excessHealth": 80,
"flags": 2048,
"probabilityToMove": 30,
"probabilityToTrack": 20,
"probabilityToShoot": 7,
"actionDelay": 15
},
{
"Class": "Cyborg",
"Skin": "80653cff",
"Arms": "9c9c9cff",
"Body": "58589cff",
"Legs": "9c9c9cff",
"Hair": "5f76ffff",
"Feet": "9c9c9cff",
"Facehair": "5f76ffff",
"Hat": "5f76ffff",
"Glasses": "5f76ffff",
"speed": 384,
"Gun": "Lasergun",
"maxHealth": 50,
"excessHealth": 100,
"flags": 0,
"probabilityToMove": 30,
"probabilityToTrack": 15,
"probabilityToShoot": 17,
"actionDelay": 15
},
{
"Class": "Jones",
"HairType": "donut",
"Skin": "d25106ff",
"Arms": "9c9c9cff",
"Body": "9c9c9cff",
"Legs": "9c9c9cff",
"Hair": "c6c6c6ff",
"Feet": "9c9c9cff",
"Facehair": "c6c6c6ff",
"Hat": "c6c6c6ff",
"Glasses": "c6c6c6ff",
"speed": 448,
"Gun": "Projector",
"maxHealth": 280,
"excessHealth": 560,
"flags": 0,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 32,
"actionDelay": 5
},
{
"Class": "Cyborg",
"GlassesType": "cyborg",
"Skin": "9c6d13ff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "001500ff",
"Feet": "363636ff",
"Facehair": "001500ff",
"Hat": "001500ff",
"Glasses": "001500ff",
"speed": 320,
"Gun": "Inkvine",
"maxHealth": 50,
"excessHealth": 100,
"flags": 1966080,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 25,
"actionDelay": 8
},
{
"Class": "Cyborg",
"HatType": "hazmat",
"Skin": "2e2e2eff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "00ff00ff",
"Feet": "363636ff",
"Facehair": "00ff00ff",
"Hat": "00ff00ff",
"Glasses": "00ff00ff",
"speed": 320,
"Gun": "Inkvine",
"maxHealth": 50,
"excessHealth": 100,
"flags": 655360,
"probabilityToMove": 40,
"probabilityToTrack": 25,
"probabilityToShoot": 25,
"actionDelay": 8
},
{
"Class": "Jones",
"PlayerTemplateName": "Kardausar Captain",
"HatType": "hazmat",
"Skin": "9c6513ff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "0ecd00ff",
"Feet": "363636ff",
"Facehair": "0ecd00ff",
"Hat": "0ecd00ff",
"Glasses": "0ecd00ff",
"speed": 192,
"Gun": "Machine gun",
"maxHealth": 20,
"excessHealth": 40,
"flags": 131072,
"probabilityToMove": 50,
"probabilityToTrack": 20,
"probabilityToShoot": 32,
"actionDelay": 15
},
{
"Class": "Jones",
"PlayerTemplateName": "Kardausar Bator",
"GlassesType": "eye_patch",
"Skin": "149c14ff",
"Arms": "363636ff",
"Body": "363636ff",
"Legs": "363636ff",
"Hair": "100f0fff",
"Feet": "363636ff",
"Facehair": "100f0fff",
"Hat": "100f0fff",
"Glasses": "100f0fff",
"speed": 192,
"Gun": "Machine gun",
"maxHealth": 20,
"excessHealth": 40,
"flags": 131072,
"probabilityToMove": 50,
"probabilityToTrack": 20,
"probabilityToShoot": 32,
"actionDelay": 15
}
]
}

View File

@@ -45,12 +45,14 @@
"MapObject": "tree_dead",
"Density": 20
}],
"PickupCounts": [],
"EnemyDensity": 26,
"Weapons": ["Khanjali",
"Machine gun",
"Inkvine",
"Lasergun"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -61,7 +63,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -72,7 +75,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -83,7 +87,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -94,7 +99,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"FillPercent": 40,
@@ -126,7 +132,7 @@
"Objectives": [{
"Description": "Destroy the Clan shield consoles",
"Type": "Destroy",
"MapObject": "terminal",
"MapObjects": ["terminal"],
"Count": 4,
"Required": 4,
"Flags": 0
@@ -134,7 +140,7 @@
{
"Description": "Destroy projectors",
"Type": "Destroy",
"MapObject": "safe",
"MapObjects": ["safe"],
"Count": 3,
"Required": 2,
"Flags": 4
@@ -142,7 +148,7 @@
{
"Description": "A discarded seeker-probe...",
"Type": "Collect",
"Pickup": "seeker_probe",
"Pickups": ["seeker_probe"],
"Count": 1,
"Required": 0,
"Flags": 5
@@ -191,6 +197,7 @@
"MapObject": "crest",
"Density": 20
}],
"PickupCounts": [],
"EnemyDensity": 11,
"Weapons": ["Khanjali",
"Machine gun",
@@ -198,7 +205,8 @@
"Shotgun",
"Lasergun",
"Dynamite"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -209,7 +217,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -220,7 +229,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -231,7 +241,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -242,7 +253,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"CorridorWidth": 2,
@@ -329,13 +341,15 @@
"MapObject": "crest",
"Density": 40
}],
"PickupCounts": [],
"EnemyDensity": 12,
"Weapons": ["Khanjali",
"Machine gun",
"Inkvine",
"Shotgun",
"Lasergun"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -346,7 +360,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -357,7 +372,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -368,7 +384,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -379,7 +396,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"CorridorWidth": 2,
@@ -444,6 +462,7 @@
"MapObject": "bulletmarks",
"Density": 25
}],
"PickupCounts": [],
"EnemyDensity": 4,
"Weapons": ["Khanjali",
"Machine gun",
@@ -452,7 +471,8 @@
"Shotgun",
"Shrapnel bombs",
"Lasergun"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -463,7 +483,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -474,7 +495,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -485,7 +507,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -496,7 +519,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"FillPercent": 46,
@@ -555,6 +579,7 @@
"MapObject": "crest",
"Density": 15
}],
"PickupCounts": [],
"EnemyDensity": 0,
"Weapons": ["Khanjali",
"Machine gun",
@@ -563,7 +588,8 @@
"Shotgun",
"Shrapnel bombs",
"Lasergun"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -574,7 +600,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -585,7 +612,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -596,7 +624,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -607,7 +636,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"Walls": 32,
@@ -656,6 +686,7 @@
"Enemies": [8],
"SpecialChars": [],
"MapObjectDensities": [],
"PickupCounts": [],
"EnemyDensity": 14,
"Weapons": ["Khanjali",
"Machine gun",
@@ -664,7 +695,8 @@
"Shotgun",
"Shrapnel bombs",
"Lasergun"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -675,7 +707,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -686,7 +719,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -697,7 +731,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -708,7 +743,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"FillPercent": 44,
@@ -748,7 +784,7 @@
{
"Description": "Recover lost cinnamon",
"Type": "Collect",
"Pickup": "bag",
"Pickups": ["bag"],
"Count": 10,
"Required": 0,
"Flags": 0
@@ -763,6 +799,7 @@
"MapObject": "scratch",
"Density": 10
}],
"PickupCounts": [],
"EnemyDensity": 12,
"Weapons": ["Khanjali",
"Machine gun",
@@ -771,7 +808,8 @@
"Shotgun",
"Shrapnel bombs",
"Lasergun"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -782,7 +820,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -793,7 +832,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -804,7 +844,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -815,7 +856,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"FillPercent": 40,
@@ -855,7 +897,7 @@
{
"Description": "Destroy their projectors",
"Type": "Destroy",
"MapObject": "safe",
"MapObjects": ["safe"],
"Count": 4,
"Required": 0,
"Flags": 4
@@ -863,7 +905,7 @@
{
"Description": "Cinnamon tea!",
"Type": "Collect",
"Pickup": "bottle_orange",
"Pickups": ["bottle_orange"],
"Count": 1,
"Required": 0,
"Flags": 0
@@ -888,6 +930,7 @@
"MapObject": "wall_stuff",
"Density": 20
}],
"PickupCounts": [],
"EnemyDensity": 9,
"Weapons": ["Khanjali",
"Machine gun",
@@ -897,7 +940,8 @@
"Shrapnel bombs",
"Molotovs",
"Lasergun"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -908,7 +952,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -919,7 +964,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -930,7 +976,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -941,7 +988,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"Walls": 18,
@@ -990,7 +1038,7 @@
{
"Description": "Collect any intelligence",
"Type": "Collect",
"Pickup": "blueprint",
"Pickups": ["blueprint"],
"Count": 3,
"Required": 0,
"Flags": 0
@@ -1023,13 +1071,15 @@
"MapObject": "plant",
"Density": 10
}],
"PickupCounts": [],
"EnemyDensity": 13,
"Weapons": ["Khanjali",
"Machine gun",
"Projector",
"Lasergun",
"Wurmtooth"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -1040,7 +1090,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -1051,7 +1102,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -1062,7 +1114,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -1073,7 +1126,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"Walls": 11,
@@ -1122,7 +1176,7 @@
{
"Description": "Destroy the fusion devices",
"Type": "Destroy",
"MapObject": "rocket",
"MapObjects": ["rocket"],
"Count": 7,
"Required": 5,
"Flags": 0
@@ -1157,6 +1211,7 @@
"MapObject": "crest",
"Density": 30
}],
"PickupCounts": [],
"EnemyDensity": 9,
"Weapons": ["Khanjali",
"Machine gun",
@@ -1170,7 +1225,8 @@
"Prox. mine",
"Dynamite",
"Wurmtooth"],
"Song": "",
"WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": {
"Wall": {
"Name": "wall",
@@ -1181,7 +1237,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Floor": {
"Name": "tile",
@@ -1192,7 +1249,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": false
"IsRoom": false,
"DamageBullet": ""
},
"Room": {
"Name": "tile",
@@ -1203,7 +1261,8 @@
"CanWalk": true,
"IsOpaque": false,
"Shootable": false,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
},
"Door": {
"Name": "door",
@@ -1214,7 +1273,8 @@
"CanWalk": false,
"IsOpaque": true,
"Shootable": true,
"IsRoom": true
"IsRoom": true,
"DamageBullet": ""
}
},
"Walls": 0,

View File

@@ -2,7 +2,7 @@
C-Dogs SDL
A port of the legendary (and fun) action/arcade cdogs.
Copyright (c) 2013-2014, 2016, 2019-2021, 2023-2024 Cong Xu
Copyright (c) 2013-2014, 2016, 2019-2021, 2023-2025 Cong Xu
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -33,7 +33,9 @@
#include "actors.h"
#include "files.h"
#include "json_utils.h"
#include "log.h"
#include "player_template.h"
#include "yajl_utils.h"
#define CHARACTER_VERSION 14
@@ -155,9 +157,11 @@ void CharacterLoadJSON(
}
else
{
LoadStr(&ch->HeadParts[HEAD_PART_FACEHAIR], child, "FacehairType");
LoadStr(
&ch->HeadParts[HEAD_PART_FACEHAIR], child, "FacehairType");
LoadStr(&ch->HeadParts[HEAD_PART_HAT], child, "HatType");
LoadStr(&ch->HeadParts[HEAD_PART_GLASSES], child, "GlassesType");
LoadStr(
&ch->HeadParts[HEAD_PART_GLASSES], child, "GlassesType");
}
}
CASSERT(ch->Class != NULL, "Cannot load character class");
@@ -247,74 +251,105 @@ void CharacterLoadJSON(
bool CharacterSave(CharacterStore *s, const char *path)
{
json_t *root = json_new_object();
AddIntPair(root, "Version", CHARACTER_VERSION);
bool res = true;
yajl_gen g = yajl_gen_alloc(NULL);
if (g == NULL)
{
LOG(LM_MAIN, LL_ERROR,
"Unable to alloc JSON generator for saving character\n");
res = false;
goto bail;
}
json_t *charNode = json_new_array();
#define YAJL_CHECK(func) \
if (func != yajl_gen_status_ok) \
{ \
LOG(LM_MAIN, LL_ERROR, "JSON generator error for character\n"); \
res = false; \
goto bail; \
}
YAJL_CHECK(yajl_gen_map_open(g));
YAJL_CHECK(YAJLAddIntPair(g, "Version", CHARACTER_VERSION));
YAJL_CHECK(yajl_gen_string(g, (const unsigned char *)"Characters", strlen("Characters")));
YAJL_CHECK(yajl_gen_array_open(g));
CA_FOREACH(Character, c, s->OtherChars)
json_t *node = json_new_object();
AddStringPair(node, "Class", c->Class->Name);
YAJL_CHECK(yajl_gen_map_open(g));
YAJL_CHECK(YAJLAddStringPair(g, "Class", c->Class->Name));
if (c->PlayerTemplateName)
{
AddStringPair(node, "PlayerTemplateName", c->PlayerTemplateName);
YAJL_CHECK(
YAJLAddStringPair(g, "PlayerTemplateName", c->PlayerTemplateName));
}
if (c->HeadParts[HEAD_PART_HAIR])
{
AddStringPair(node, "HairType", c->HeadParts[HEAD_PART_HAIR]);
YAJL_CHECK(
YAJLAddStringPair(g, "HairType", c->HeadParts[HEAD_PART_HAIR]));
}
if (c->HeadParts[HEAD_PART_FACEHAIR])
{
AddStringPair(node, "FacehairType", c->HeadParts[HEAD_PART_FACEHAIR]);
YAJL_CHECK(YAJLAddStringPair(
g, "FacehairType", c->HeadParts[HEAD_PART_FACEHAIR]));
}
if (c->HeadParts[HEAD_PART_HAT])
{
AddStringPair(node, "HatType", c->HeadParts[HEAD_PART_HAT]);
YAJL_CHECK(
YAJLAddStringPair(g, "HatType", c->HeadParts[HEAD_PART_HAT]));
}
if (c->HeadParts[HEAD_PART_GLASSES])
{
AddStringPair(node, "GlassesType", c->HeadParts[HEAD_PART_GLASSES]);
YAJL_CHECK(YAJLAddStringPair(
g, "GlassesType", c->HeadParts[HEAD_PART_GLASSES]));
}
AddColorPair(node, "Skin", c->Colors.Skin);
AddColorPair(node, "Arms", c->Colors.Arms);
AddColorPair(node, "Body", c->Colors.Body);
AddColorPair(node, "Legs", c->Colors.Legs);
AddColorPair(node, "Hair", c->Colors.Hair);
AddColorPair(node, "Feet", c->Colors.Feet);
AddColorPair(node, "Facehair", c->Colors.Facehair);
AddColorPair(node, "Hat", c->Colors.Hat);
AddColorPair(node, "Glasses", c->Colors.Glasses);
AddIntPair(node, "speed", (int)(c->speed * 256));
json_insert_pair_into_object(node, "Gun", json_new_string(c->Gun->name));
YAJL_CHECK(YAJLAddColorPair(g, "Skin", c->Colors.Skin));
YAJL_CHECK(YAJLAddColorPair(g, "Arms", c->Colors.Arms));
YAJL_CHECK(YAJLAddColorPair(g, "Body", c->Colors.Body));
YAJL_CHECK(YAJLAddColorPair(g, "Legs", c->Colors.Legs));
YAJL_CHECK(YAJLAddColorPair(g, "Hair", c->Colors.Hair));
YAJL_CHECK(YAJLAddColorPair(g, "Feet", c->Colors.Feet));
YAJL_CHECK(YAJLAddColorPair(g, "Facehair", c->Colors.Facehair));
YAJL_CHECK(YAJLAddColorPair(g, "Hat", c->Colors.Hat));
YAJL_CHECK(YAJLAddColorPair(g, "Glasses", c->Colors.Glasses));
YAJL_CHECK(YAJLAddIntPair(g, "speed", (int)(c->speed * 256)));
YAJL_CHECK(YAJLAddStringPair(g, "Gun", c->Gun->name));
if (c->Melee != NULL)
{
json_insert_pair_into_object(node, "Melee", json_new_string(c->Melee->name));
YAJL_CHECK(YAJLAddStringPair(g, "Melee", c->Melee->name));
}
AddIntPair(node, "maxHealth", c->maxHealth);
AddIntPair(node, "excessHealth", c->excessHealth);
AddIntPair(node, "flags", c->flags);
YAJL_CHECK(YAJLAddIntPair(g, "maxHealth", c->maxHealth));
YAJL_CHECK(YAJLAddIntPair(g, "excessHealth", c->excessHealth));
YAJL_CHECK(YAJLAddIntPair(g, "flags", c->flags));
if (c->Drop != NULL)
{
json_insert_pair_into_object(
node, "Drop", json_new_string(c->Drop->Name));
YAJL_CHECK(YAJLAddStringPair(g, "Drop", c->Drop->Name));
}
AddIntPair(node, "probabilityToMove", c->bot->probabilityToMove);
AddIntPair(node, "probabilityToTrack", c->bot->probabilityToTrack);
AddIntPair(node, "probabilityToShoot", c->bot->probabilityToShoot);
AddIntPair(node, "actionDelay", c->bot->actionDelay);
json_insert_child(charNode, node);
YAJL_CHECK(
YAJLAddIntPair(g, "probabilityToMove", c->bot->probabilityToMove));
YAJL_CHECK(
YAJLAddIntPair(g, "probabilityToTrack", c->bot->probabilityToTrack));
YAJL_CHECK(
YAJLAddIntPair(g, "probabilityToShoot", c->bot->probabilityToShoot));
YAJL_CHECK(YAJLAddIntPair(g, "actionDelay", c->bot->actionDelay));
YAJL_CHECK(yajl_gen_map_close(g));
CA_FOREACH_END()
json_insert_pair_into_object(root, "Characters", charNode);
YAJL_CHECK(yajl_gen_array_close(g));
YAJL_CHECK(yajl_gen_map_close(g));
char buf[CDOGS_PATH_MAX];
sprintf(buf, "%s/characters.json", path);
if (!TrySaveJSONFile(root, buf))
if (!YAJLTrySaveJSONFile(g, buf))
{
res = false;
goto bail;
}
bail:
json_free_value(&root);
if (g)
{
yajl_gen_clear(g);
yajl_gen_free(g);
}
return res;
}
@@ -393,7 +428,7 @@ bool CharacterIsPrisoner(const CharacterStore *store, const Character *c)
void CharacterSetHeadPart(Character *c, const HeadPart hp, const char *name)
{
CFREE(c->HeadParts[hp]);
c->HeadParts[hp] = NULL;
c->HeadParts[hp] = NULL;
if (name)
{
CSTRDUP(c->HeadParts[hp], name);
@@ -417,11 +452,11 @@ void CharacterShuffleAppearance(Character *c)
&gCharacterClasses.CustomClasses,
charClass - gCharacterClasses.Classes.size);
}
for (HeadPart hp = HEAD_PART_HAIR; hp < HEAD_PART_COUNT; hp++)
{
const char *name = NULL;
if (RAND_INT(0, 3)==0)
if (RAND_INT(0, 3) == 0)
{
const CArray *hpNames = &gPicManager.headPartNames[hp];
name = *(char **)CArrayGet(hpNames, rand() % hpNames->size);

View File

@@ -25,6 +25,8 @@
*/
#include "yajl_utils.h"
#include "log.h"
#include <stdio.h>
#include <stdlib.h>
@@ -245,3 +247,32 @@ bail:
CFREE(pathCopy);
return out;
}
bool YAJLTrySaveJSONFile(yajl_gen g, const char *filename)
{
const char *buf;
size_t len;
bool res = true;
yajl_gen_get_buf(g, (const unsigned char **)&buf, &len);
FILE *f = fopen(filename, "w");
if (f == NULL)
{
LOG(LM_MAIN, LL_ERROR, "Unable to save %s\n", filename);
res = false;
goto bail;
}
fwrite(buf, 1, len, f);
#ifdef __EMSCRIPTEN__
EM_ASM(
// persist changes
FS.syncfs(false, function(err) { assert(!err); }););
#endif
bail:
if (f != NULL)
{
fclose(f);
}
return res;
}

View File

@@ -31,19 +31,21 @@
#include "yajl/api/yajl_gen.h"
#include "yajl/api/yajl_tree.h"
yajl_val YAJLReadFile(const char *filename);
yajl_gen_status YAJLAddIntPair(yajl_gen g, const char *name, const int number);
yajl_gen_status YAJLAddBoolPair(yajl_gen g, const char *name, const bool value);
yajl_gen_status YAJLAddBoolPair(
yajl_gen g, const char *name, const bool value);
yajl_gen_status YAJLAddStringPair(yajl_gen g, const char *name, const char *s);
yajl_gen_status YAJLAddColorPair(yajl_gen g, const char *name, const color_t c);
yajl_gen_status YAJLAddColorPair(
yajl_gen g, const char *name, const color_t c);
void YAJLBool(bool *value, yajl_val node, const char *name);
void YAJLInt(int *value, yajl_val node, const char *name);
void YAJLDouble(double *value, yajl_val node, const char *name);
void YAJLVec2i(struct vec2i *value, yajl_val node, const char *name);
#define YAJL_GET_VEC2I(v) svec2i(\
(int)YAJL_GET_INTEGER(YAJL_GET_ARRAY(v)->values[0]),\
(int)YAJL_GET_INTEGER(YAJL_GET_ARRAY(v)->values[1]))
#define YAJL_GET_VEC2I(v) \
svec2i( \
(int)YAJL_GET_INTEGER(YAJL_GET_ARRAY(v)->values[0]), \
(int)YAJL_GET_INTEGER(YAJL_GET_ARRAY(v)->values[1]))
// remember to free
void YAJLStr(char **value, yajl_val node, const char *name);
char *YAJLGetStr(yajl_val node, const char *name);
@@ -74,3 +76,5 @@ bool YAJLTryLoadValue(yajl_val *node, const char *name);
}\
}
*/
bool YAJLTrySaveJSONFile(yajl_gen g, const char *filename);

View File

@@ -499,7 +499,12 @@ static void Save(void)
BlitUpdateFromBuf(&gGraphicsDevice, gGraphicsDevice.screen);
WindowContextPostRender(&gGraphicsDevice.gameWindow);
MapArchiveSave(buf, &gCampaign.Setting);
if (!MapArchiveSave(buf, &gCampaign.Setting))
{
SDL_ShowSimpleMessageBox(
SDL_MESSAGEBOX_INFORMATION, "Error", "Error when saving campaign!",
gGraphicsDevice.gameWindow.window);
}
fileChanged = false;
strcpy(lastFile, buf);
sAutosaveIndex = 0;