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", "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.", "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, "Ammo": false,
"WeaponPersist": false,
"SkipWeaponMenu": false, "SkipWeaponMenu": false,
"BuyAndSell": false,
"RandomPickups": true, "RandomPickups": true,
"DoorOpenTicks": 70,
"Lives": 0,
"MaxLives": 0,
"PlayerHP": 0,
"PlayerMaxHP": 0,
"PlayerExcessHP": 0,
"Missions": 10 "Missions": 10
} }

View File

@@ -1,327 +1,403 @@
{ {
"Version": 13, "Version": 14,
"Characters": [{ "Characters": [
"Class": "Ogre", {
"HairType": "mohawk", "Class": "Ogre",
"Skin": "9c589cff", "HairType": "mohawk",
"Arms": "9c589cff", "Skin": "9c589cff",
"Body": "9c5858ff", "Arms": "9c589cff",
"Legs": "9c5858ff", "Body": "9c5858ff",
"Hair": "9c0000ff", "Legs": "9c5858ff",
"Feet": "9c5858ff", "Hair": "9c0000ff",
"speed": 384, "Feet": "9c5858ff",
"Gun": "Monster Bite", "Facehair": "9c0000ff",
"maxHealth": 40, "Hat": "9c0000ff",
"flags": 0, "Glasses": "9c0000ff",
"probabilityToMove": 70, "speed": 384,
"probabilityToTrack": 30, "Gun": "Monster Bite",
"probabilityToShoot": 0, "maxHealth": 40,
"actionDelay": 15 "excessHealth": 80,
}, "flags": 0,
{ "probabilityToMove": 70,
"Class": "Ogre", "probabilityToTrack": 30,
"Skin": "d25106ff", "probabilityToShoot": 0,
"Arms": "ff8136ff", "actionDelay": 15
"Body": "ff8136ff", },
"Legs": "ff8136ff", {
"Hair": "c00000ff", "Class": "Ogre",
"Feet": "ff8136ff", "Skin": "d25106ff",
"speed": 320, "Arms": "ff8136ff",
"Gun": "Monster Bite", "Body": "ff8136ff",
"maxHealth": 20, "Legs": "ff8136ff",
"flags": 0, "Hair": "c00000ff",
"probabilityToMove": 60, "Feet": "ff8136ff",
"probabilityToTrack": 25, "Facehair": "c00000ff",
"probabilityToShoot": 0, "Hat": "c00000ff",
"actionDelay": 15 "Glasses": "c00000ff",
}, "speed": 320,
{ "Gun": "Monster Bite",
"Class": "Jones", "maxHealth": 20,
"HairType": "flattop", "excessHealth": 40,
"Skin": "ff8136ff", "flags": 0,
"Arms": "b79536ff", "probabilityToMove": 60,
"Body": "b79536ff", "probabilityToTrack": 25,
"Legs": "b79536ff", "probabilityToShoot": 0,
"Hair": "882800ff", "actionDelay": 15
"Feet": "b79536ff", },
"speed": 256, {
"Gun": "Machine gun", "Class": "Jones",
"maxHealth": 20, "HairType": "flattop",
"flags": 0, "Skin": "ff8136ff",
"probabilityToMove": 25, "Arms": "b79536ff",
"probabilityToTrack": 15, "Body": "b79536ff",
"probabilityToShoot": 27, "Legs": "b79536ff",
"actionDelay": 15 "Hair": "882800ff",
}, "Feet": "b79536ff",
{ "Facehair": "882800ff",
"Class": "Soldier", "Hat": "882800ff",
"HairType": "peak_cap", "Glasses": "882800ff",
"Skin": "ff8136ff", "speed": 256,
"Arms": "b79536ff", "Gun": "Machine gun",
"Body": "b79536ff", "maxHealth": 20,
"Legs": "b79536ff", "excessHealth": 40,
"Hair": "b98c00ff", "flags": 0,
"Feet": "b79536ff", "probabilityToMove": 25,
"speed": 256, "probabilityToTrack": 15,
"Gun": "Machine gun", "probabilityToShoot": 27,
"maxHealth": 30, "actionDelay": 15
"flags": 0, },
"probabilityToMove": 20, {
"probabilityToTrack": 15, "Class": "Soldier",
"probabilityToShoot": 37, "HatType": "peak_cap",
"actionDelay": 15 "Skin": "ff8136ff",
}, "Arms": "b79536ff",
{ "Body": "b79536ff",
"Class": "Jones", "Legs": "b79536ff",
"HairType": "eye_patch", "Hair": "b98c00ff",
"Skin": "ff8136ff", "Feet": "b79536ff",
"Arms": "b79536ff", "Facehair": "b98c00ff",
"Body": "b79536ff", "Hat": "b98c00ff",
"Legs": "b79536ff", "Glasses": "b98c00ff",
"Hair": "000000ff", "speed": 256,
"Feet": "b79536ff", "Gun": "Machine gun",
"speed": 320, "maxHealth": 30,
"Gun": "Khanjali", "excessHealth": 60,
"maxHealth": 30, "flags": 0,
"flags": 0, "probabilityToMove": 20,
"probabilityToMove": 50, "probabilityToTrack": 15,
"probabilityToTrack": 15, "probabilityToShoot": 37,
"probabilityToShoot": 0, "actionDelay": 15
"actionDelay": 15 },
}, {
{ "Class": "Jones",
"Class": "Cyborg", "GlassesType": "eye_patch",
"PlayerTemplateName": "Kardausar Colonel", "Skin": "ff8136ff",
"HairType": "cyborg", "Arms": "b79536ff",
"Skin": "e29b31ff", "Body": "b79536ff",
"Arms": "363636ff", "Legs": "b79536ff",
"Body": "363636ff", "Hair": "000000ff",
"Legs": "363636ff", "Feet": "b79536ff",
"Hair": "052020ff", "Facehair": "000000ff",
"Feet": "363636ff", "Hat": "000000ff",
"speed": 320, "Glasses": "000000ff",
"Gun": "Lasergun", "speed": 320,
"maxHealth": 40, "Gun": "Khanjali",
"flags": 131072, "maxHealth": 30,
"probabilityToMove": 40, "excessHealth": 60,
"probabilityToTrack": 25, "flags": 0,
"probabilityToShoot": 27, "probabilityToMove": 50,
"actionDelay": 15 "probabilityToTrack": 15,
}, "probabilityToShoot": 0,
{ "actionDelay": 15
"Class": "Jones", },
"PlayerTemplateName": "Kardausar Private", {
"HairType": "hazmat", "Class": "Cyborg",
"Skin": "3f3f3fff", "PlayerTemplateName": "Kardausar Colonel",
"Arms": "363636ff", "GlassesType": "cyborg",
"Body": "363636ff", "Skin": "e29b31ff",
"Legs": "363636ff", "Arms": "363636ff",
"Hair": "38ff35ff", "Body": "363636ff",
"Feet": "363636ff", "Legs": "363636ff",
"speed": 192, "Hair": "052020ff",
"Gun": "Machine gun", "Feet": "363636ff",
"maxHealth": 20, "Facehair": "052020ff",
"flags": 131072, "Hat": "052020ff",
"probabilityToMove": 50, "Glasses": "052020ff",
"probabilityToTrack": 20, "speed": 320,
"probabilityToShoot": 32, "Gun": "Lasergun",
"actionDelay": 15 "maxHealth": 40,
}, "excessHealth": 80,
{ "flags": 131072,
"Class": "Jones", "probabilityToMove": 40,
"HairType": "flattop", "probabilityToTrack": 25,
"Skin": "ff8136ff", "probabilityToShoot": 27,
"Arms": "b79536ff", "actionDelay": 15
"Body": "9c9c9cff", },
"Legs": "b79536ff", {
"Hair": "ff6c00ff", "Class": "Jones",
"Feet": "b79536ff", "PlayerTemplateName": "Kardausar Private",
"speed": 448, "HatType": "hazmat",
"Gun": "Projector", "Skin": "3f3f3fff",
"maxHealth": 180, "Arms": "363636ff",
"flags": 0, "Body": "363636ff",
"probabilityToMove": 40, "Legs": "363636ff",
"probabilityToTrack": 25, "Hair": "38ff35ff",
"probabilityToShoot": 37, "Feet": "363636ff",
"actionDelay": 3 "Facehair": "38ff35ff",
}, "Hat": "38ff35ff",
{ "Glasses": "38ff35ff",
"Class": "Jones", "speed": 192,
"HairType": "beard", "Gun": "Machine gun",
"Skin": "ffffffff", "maxHealth": 20,
"Arms": "9c9c9cff", "excessHealth": 40,
"Body": "9c9c9cff", "flags": 131072,
"Legs": "9c9c9cff", "probabilityToMove": 50,
"Hair": "27282aff", "probabilityToTrack": 20,
"Feet": "9c9c9cff", "probabilityToShoot": 32,
"speed": 320, "actionDelay": 15
"Gun": "Wurmtooth", },
"maxHealth": 30, {
"flags": 2048, "Class": "Jones",
"probabilityToMove": 50, "HairType": "flattop",
"probabilityToTrack": 15, "Skin": "ff8136ff",
"probabilityToShoot": 0, "Arms": "b79536ff",
"actionDelay": 15 "Body": "9c9c9cff",
}, "Legs": "b79536ff",
{ "Hair": "ff6c00ff",
"Class": "Cyborg", "Feet": "b79536ff",
"Skin": "882800ff", "Facehair": "ff6c00ff",
"Arms": "9c9c9cff", "Hat": "ff6c00ff",
"Body": "9c9c9cff", "Glasses": "ff6c00ff",
"Legs": "9c9c9cff", "speed": 448,
"Hair": "4b6affff", "Gun": "Projector",
"Feet": "9c9c9cff", "maxHealth": 180,
"speed": 320, "excessHealth": 360,
"Gun": "Wurmtooth", "flags": 0,
"maxHealth": 30, "probabilityToMove": 40,
"flags": 0, "probabilityToTrack": 25,
"probabilityToMove": 50, "probabilityToShoot": 37,
"probabilityToTrack": 15, "actionDelay": 3
"probabilityToShoot": 0, },
"actionDelay": 15 {
}, "Class": "Jones",
{ "HairType": "flattop",
"Class": "Cyborg", "FacehairType": "beard",
"Skin": "d25106ff", "Skin": "ffffffff",
"Arms": "9c9c9cff", "Arms": "9c9c9cff",
"Body": "9c0000ff", "Body": "9c9c9cff",
"Legs": "9c9c9cff", "Legs": "9c9c9cff",
"Hair": "3ba3ffff", "Hair": "27282aff",
"Feet": "9c9c9cff", "Feet": "9c9c9cff",
"speed": 320, "Facehair": "27282aff",
"Gun": "Projector", "Hat": "27282aff",
"maxHealth": 40, "Glasses": "27282aff",
"flags": 0, "speed": 320,
"probabilityToMove": 30, "Gun": "Wurmtooth",
"probabilityToTrack": 15, "maxHealth": 30,
"probabilityToShoot": 7, "excessHealth": 60,
"actionDelay": 15 "flags": 2048,
}, "probabilityToMove": 50,
{ "probabilityToTrack": 15,
"Class": "Jones", "probabilityToShoot": 0,
"HairType": "beard", "actionDelay": 15
"Skin": "ffffffff", },
"Arms": "9c9c9cff", {
"Body": "9c0000ff", "Class": "Cyborg",
"Legs": "9c9c9cff", "Skin": "882800ff",
"Hair": "141415ff", "Arms": "9c9c9cff",
"Feet": "9c9c9cff", "Body": "9c9c9cff",
"speed": 320, "Legs": "9c9c9cff",
"Gun": "Projector", "Hair": "4b6affff",
"maxHealth": 40, "Feet": "9c9c9cff",
"flags": 2048, "Facehair": "4b6affff",
"probabilityToMove": 30, "Hat": "4b6affff",
"probabilityToTrack": 20, "Glasses": "4b6affff",
"probabilityToShoot": 7, "speed": 320,
"actionDelay": 15 "Gun": "Wurmtooth",
}, "maxHealth": 30,
{ "excessHealth": 60,
"Class": "Cyborg", "flags": 0,
"Skin": "80653cff", "probabilityToMove": 50,
"Arms": "9c9c9cff", "probabilityToTrack": 15,
"Body": "58589cff", "probabilityToShoot": 0,
"Legs": "9c9c9cff", "actionDelay": 15
"Hair": "5f76ffff", },
"Feet": "9c9c9cff", {
"speed": 384, "Class": "Cyborg",
"Gun": "Lasergun", "Skin": "d25106ff",
"maxHealth": 50, "Arms": "9c9c9cff",
"flags": 0, "Body": "9c0000ff",
"probabilityToMove": 30, "Legs": "9c9c9cff",
"probabilityToTrack": 15, "Hair": "3ba3ffff",
"probabilityToShoot": 17, "Feet": "9c9c9cff",
"actionDelay": 15 "Facehair": "3ba3ffff",
}, "Hat": "3ba3ffff",
{ "Glasses": "3ba3ffff",
"Class": "Jones", "speed": 320,
"HairType": "donut", "Gun": "Projector",
"Skin": "d25106ff", "maxHealth": 40,
"Arms": "9c9c9cff", "excessHealth": 80,
"Body": "9c9c9cff", "flags": 0,
"Legs": "9c9c9cff", "probabilityToMove": 30,
"Hair": "c6c6c6ff", "probabilityToTrack": 15,
"Feet": "9c9c9cff", "probabilityToShoot": 7,
"speed": 448, "actionDelay": 15
"Gun": "Projector", },
"maxHealth": 280, {
"flags": 0, "Class": "Jones",
"probabilityToMove": 40, "HairType": "flattop",
"probabilityToTrack": 25, "FacehairType": "beard",
"probabilityToShoot": 32, "Skin": "ffffffff",
"actionDelay": 5 "Arms": "9c9c9cff",
}, "Body": "9c0000ff",
{ "Legs": "9c9c9cff",
"Class": "Cyborg", "Hair": "141415ff",
"HairType": "cyborg", "Feet": "9c9c9cff",
"Skin": "9c6d13ff", "Facehair": "141415ff",
"Arms": "363636ff", "Hat": "141415ff",
"Body": "363636ff", "Glasses": "141415ff",
"Legs": "363636ff", "speed": 320,
"Hair": "001500ff", "Gun": "Projector",
"Feet": "363636ff", "maxHealth": 40,
"speed": 320, "excessHealth": 80,
"Gun": "Inkvine", "flags": 2048,
"maxHealth": 50, "probabilityToMove": 30,
"flags": 1966080, "probabilityToTrack": 20,
"probabilityToMove": 40, "probabilityToShoot": 7,
"probabilityToTrack": 25, "actionDelay": 15
"probabilityToShoot": 25, },
"actionDelay": 8 {
}, "Class": "Cyborg",
{ "Skin": "80653cff",
"Class": "Cyborg", "Arms": "9c9c9cff",
"HairType": "hazmat", "Body": "58589cff",
"Skin": "2e2e2eff", "Legs": "9c9c9cff",
"Arms": "363636ff", "Hair": "5f76ffff",
"Body": "363636ff", "Feet": "9c9c9cff",
"Legs": "363636ff", "Facehair": "5f76ffff",
"Hair": "00ff00ff", "Hat": "5f76ffff",
"Feet": "363636ff", "Glasses": "5f76ffff",
"speed": 320, "speed": 384,
"Gun": "Inkvine", "Gun": "Lasergun",
"maxHealth": 50, "maxHealth": 50,
"flags": 655360, "excessHealth": 100,
"probabilityToMove": 40, "flags": 0,
"probabilityToTrack": 25, "probabilityToMove": 30,
"probabilityToShoot": 25, "probabilityToTrack": 15,
"actionDelay": 8 "probabilityToShoot": 17,
}, "actionDelay": 15
{ },
"Class": "Jones", {
"PlayerTemplateName": "Kardausar Captain", "Class": "Jones",
"HairType": "hazmat", "HairType": "donut",
"Skin": "9c6513ff", "Skin": "d25106ff",
"Arms": "363636ff", "Arms": "9c9c9cff",
"Body": "363636ff", "Body": "9c9c9cff",
"Legs": "363636ff", "Legs": "9c9c9cff",
"Hair": "0ecd00ff", "Hair": "c6c6c6ff",
"Feet": "363636ff", "Feet": "9c9c9cff",
"speed": 192, "Facehair": "c6c6c6ff",
"Gun": "Machine gun", "Hat": "c6c6c6ff",
"maxHealth": 20, "Glasses": "c6c6c6ff",
"flags": 131072, "speed": 448,
"probabilityToMove": 50, "Gun": "Projector",
"probabilityToTrack": 20, "maxHealth": 280,
"probabilityToShoot": 32, "excessHealth": 560,
"actionDelay": 15 "flags": 0,
}, "probabilityToMove": 40,
{ "probabilityToTrack": 25,
"Class": "Jones", "probabilityToShoot": 32,
"PlayerTemplateName": "Kardausar Bator", "actionDelay": 5
"HairType": "eye_patch", },
"Skin": "149c14ff", {
"Arms": "363636ff", "Class": "Cyborg",
"Body": "363636ff", "GlassesType": "cyborg",
"Legs": "363636ff", "Skin": "9c6d13ff",
"Hair": "100f0fff", "Arms": "363636ff",
"Feet": "363636ff", "Body": "363636ff",
"speed": 192, "Legs": "363636ff",
"Gun": "Machine gun", "Hair": "001500ff",
"maxHealth": 20, "Feet": "363636ff",
"flags": 131072, "Facehair": "001500ff",
"probabilityToMove": 50, "Hat": "001500ff",
"probabilityToTrack": 20, "Glasses": "001500ff",
"probabilityToShoot": 32, "speed": 320,
"actionDelay": 15 "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", "MapObject": "tree_dead",
"Density": 20 "Density": 20
}], }],
"PickupCounts": [],
"EnemyDensity": 26, "EnemyDensity": 26,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
"Inkvine", "Inkvine",
"Lasergun"], "Lasergun"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -61,7 +63,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -72,7 +75,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -83,7 +87,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -94,7 +99,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"FillPercent": 40, "FillPercent": 40,
@@ -126,7 +132,7 @@
"Objectives": [{ "Objectives": [{
"Description": "Destroy the Clan shield consoles", "Description": "Destroy the Clan shield consoles",
"Type": "Destroy", "Type": "Destroy",
"MapObject": "terminal", "MapObjects": ["terminal"],
"Count": 4, "Count": 4,
"Required": 4, "Required": 4,
"Flags": 0 "Flags": 0
@@ -134,7 +140,7 @@
{ {
"Description": "Destroy projectors", "Description": "Destroy projectors",
"Type": "Destroy", "Type": "Destroy",
"MapObject": "safe", "MapObjects": ["safe"],
"Count": 3, "Count": 3,
"Required": 2, "Required": 2,
"Flags": 4 "Flags": 4
@@ -142,7 +148,7 @@
{ {
"Description": "A discarded seeker-probe...", "Description": "A discarded seeker-probe...",
"Type": "Collect", "Type": "Collect",
"Pickup": "seeker_probe", "Pickups": ["seeker_probe"],
"Count": 1, "Count": 1,
"Required": 0, "Required": 0,
"Flags": 5 "Flags": 5
@@ -191,6 +197,7 @@
"MapObject": "crest", "MapObject": "crest",
"Density": 20 "Density": 20
}], }],
"PickupCounts": [],
"EnemyDensity": 11, "EnemyDensity": 11,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
@@ -198,7 +205,8 @@
"Shotgun", "Shotgun",
"Lasergun", "Lasergun",
"Dynamite"], "Dynamite"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -209,7 +217,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -220,7 +229,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -231,7 +241,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -242,7 +253,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"CorridorWidth": 2, "CorridorWidth": 2,
@@ -329,13 +341,15 @@
"MapObject": "crest", "MapObject": "crest",
"Density": 40 "Density": 40
}], }],
"PickupCounts": [],
"EnemyDensity": 12, "EnemyDensity": 12,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
"Inkvine", "Inkvine",
"Shotgun", "Shotgun",
"Lasergun"], "Lasergun"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -346,7 +360,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -357,7 +372,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -368,7 +384,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -379,7 +396,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"CorridorWidth": 2, "CorridorWidth": 2,
@@ -444,6 +462,7 @@
"MapObject": "bulletmarks", "MapObject": "bulletmarks",
"Density": 25 "Density": 25
}], }],
"PickupCounts": [],
"EnemyDensity": 4, "EnemyDensity": 4,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
@@ -452,7 +471,8 @@
"Shotgun", "Shotgun",
"Shrapnel bombs", "Shrapnel bombs",
"Lasergun"], "Lasergun"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -463,7 +483,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -474,7 +495,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -485,7 +507,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -496,7 +519,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"FillPercent": 46, "FillPercent": 46,
@@ -555,6 +579,7 @@
"MapObject": "crest", "MapObject": "crest",
"Density": 15 "Density": 15
}], }],
"PickupCounts": [],
"EnemyDensity": 0, "EnemyDensity": 0,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
@@ -563,7 +588,8 @@
"Shotgun", "Shotgun",
"Shrapnel bombs", "Shrapnel bombs",
"Lasergun"], "Lasergun"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -574,7 +600,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -585,7 +612,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -596,7 +624,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -607,7 +636,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"Walls": 32, "Walls": 32,
@@ -656,6 +686,7 @@
"Enemies": [8], "Enemies": [8],
"SpecialChars": [], "SpecialChars": [],
"MapObjectDensities": [], "MapObjectDensities": [],
"PickupCounts": [],
"EnemyDensity": 14, "EnemyDensity": 14,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
@@ -664,7 +695,8 @@
"Shotgun", "Shotgun",
"Shrapnel bombs", "Shrapnel bombs",
"Lasergun"], "Lasergun"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -675,7 +707,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -686,7 +719,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -697,7 +731,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -708,7 +743,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"FillPercent": 44, "FillPercent": 44,
@@ -748,7 +784,7 @@
{ {
"Description": "Recover lost cinnamon", "Description": "Recover lost cinnamon",
"Type": "Collect", "Type": "Collect",
"Pickup": "bag", "Pickups": ["bag"],
"Count": 10, "Count": 10,
"Required": 0, "Required": 0,
"Flags": 0 "Flags": 0
@@ -763,6 +799,7 @@
"MapObject": "scratch", "MapObject": "scratch",
"Density": 10 "Density": 10
}], }],
"PickupCounts": [],
"EnemyDensity": 12, "EnemyDensity": 12,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
@@ -771,7 +808,8 @@
"Shotgun", "Shotgun",
"Shrapnel bombs", "Shrapnel bombs",
"Lasergun"], "Lasergun"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -782,7 +820,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -793,7 +832,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -804,7 +844,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -815,7 +856,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"FillPercent": 40, "FillPercent": 40,
@@ -855,7 +897,7 @@
{ {
"Description": "Destroy their projectors", "Description": "Destroy their projectors",
"Type": "Destroy", "Type": "Destroy",
"MapObject": "safe", "MapObjects": ["safe"],
"Count": 4, "Count": 4,
"Required": 0, "Required": 0,
"Flags": 4 "Flags": 4
@@ -863,7 +905,7 @@
{ {
"Description": "Cinnamon tea!", "Description": "Cinnamon tea!",
"Type": "Collect", "Type": "Collect",
"Pickup": "bottle_orange", "Pickups": ["bottle_orange"],
"Count": 1, "Count": 1,
"Required": 0, "Required": 0,
"Flags": 0 "Flags": 0
@@ -888,6 +930,7 @@
"MapObject": "wall_stuff", "MapObject": "wall_stuff",
"Density": 20 "Density": 20
}], }],
"PickupCounts": [],
"EnemyDensity": 9, "EnemyDensity": 9,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
@@ -897,7 +940,8 @@
"Shrapnel bombs", "Shrapnel bombs",
"Molotovs", "Molotovs",
"Lasergun"], "Lasergun"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -908,7 +952,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -919,7 +964,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -930,7 +976,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -941,7 +988,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"Walls": 18, "Walls": 18,
@@ -990,7 +1038,7 @@
{ {
"Description": "Collect any intelligence", "Description": "Collect any intelligence",
"Type": "Collect", "Type": "Collect",
"Pickup": "blueprint", "Pickups": ["blueprint"],
"Count": 3, "Count": 3,
"Required": 0, "Required": 0,
"Flags": 0 "Flags": 0
@@ -1023,13 +1071,15 @@
"MapObject": "plant", "MapObject": "plant",
"Density": 10 "Density": 10
}], }],
"PickupCounts": [],
"EnemyDensity": 13, "EnemyDensity": 13,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
"Projector", "Projector",
"Lasergun", "Lasergun",
"Wurmtooth"], "Wurmtooth"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -1040,7 +1090,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -1051,7 +1102,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -1062,7 +1114,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -1073,7 +1126,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"Walls": 11, "Walls": 11,
@@ -1122,7 +1176,7 @@
{ {
"Description": "Destroy the fusion devices", "Description": "Destroy the fusion devices",
"Type": "Destroy", "Type": "Destroy",
"MapObject": "rocket", "MapObjects": ["rocket"],
"Count": 7, "Count": 7,
"Required": 5, "Required": 5,
"Flags": 0 "Flags": 0
@@ -1157,6 +1211,7 @@
"MapObject": "crest", "MapObject": "crest",
"Density": 30 "Density": 30
}], }],
"PickupCounts": [],
"EnemyDensity": 9, "EnemyDensity": 9,
"Weapons": ["Khanjali", "Weapons": ["Khanjali",
"Machine gun", "Machine gun",
@@ -1170,7 +1225,8 @@
"Prox. mine", "Prox. mine",
"Dynamite", "Dynamite",
"Wurmtooth"], "Wurmtooth"],
"Song": "", "WeaponPersist": false,
"SkipDebrief": false,
"TileClasses": { "TileClasses": {
"Wall": { "Wall": {
"Name": "wall", "Name": "wall",
@@ -1181,7 +1237,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Floor": { "Floor": {
"Name": "tile", "Name": "tile",
@@ -1192,7 +1249,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": false "IsRoom": false,
"DamageBullet": ""
}, },
"Room": { "Room": {
"Name": "tile", "Name": "tile",
@@ -1203,7 +1261,8 @@
"CanWalk": true, "CanWalk": true,
"IsOpaque": false, "IsOpaque": false,
"Shootable": false, "Shootable": false,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
}, },
"Door": { "Door": {
"Name": "door", "Name": "door",
@@ -1214,7 +1273,8 @@
"CanWalk": false, "CanWalk": false,
"IsOpaque": true, "IsOpaque": true,
"Shootable": true, "Shootable": true,
"IsRoom": true "IsRoom": true,
"DamageBullet": ""
} }
}, },
"Walls": 0, "Walls": 0,

View File

@@ -2,7 +2,7 @@
C-Dogs SDL C-Dogs SDL
A port of the legendary (and fun) action/arcade cdogs. 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. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -33,7 +33,9 @@
#include "actors.h" #include "actors.h"
#include "files.h" #include "files.h"
#include "json_utils.h" #include "json_utils.h"
#include "log.h"
#include "player_template.h" #include "player_template.h"
#include "yajl_utils.h"
#define CHARACTER_VERSION 14 #define CHARACTER_VERSION 14
@@ -155,9 +157,11 @@ void CharacterLoadJSON(
} }
else 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_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"); CASSERT(ch->Class != NULL, "Cannot load character class");
@@ -247,74 +251,105 @@ void CharacterLoadJSON(
bool CharacterSave(CharacterStore *s, const char *path) bool CharacterSave(CharacterStore *s, const char *path)
{ {
json_t *root = json_new_object();
AddIntPair(root, "Version", CHARACTER_VERSION);
bool res = true; 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) CA_FOREACH(Character, c, s->OtherChars)
json_t *node = json_new_object(); YAJL_CHECK(yajl_gen_map_open(g));
AddStringPair(node, "Class", c->Class->Name); YAJL_CHECK(YAJLAddStringPair(g, "Class", c->Class->Name));
if (c->PlayerTemplateName) if (c->PlayerTemplateName)
{ {
AddStringPair(node, "PlayerTemplateName", c->PlayerTemplateName); YAJL_CHECK(
YAJLAddStringPair(g, "PlayerTemplateName", c->PlayerTemplateName));
} }
if (c->HeadParts[HEAD_PART_HAIR]) 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]) 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]) 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]) 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); YAJL_CHECK(YAJLAddColorPair(g, "Skin", c->Colors.Skin));
AddColorPair(node, "Arms", c->Colors.Arms); YAJL_CHECK(YAJLAddColorPair(g, "Arms", c->Colors.Arms));
AddColorPair(node, "Body", c->Colors.Body); YAJL_CHECK(YAJLAddColorPair(g, "Body", c->Colors.Body));
AddColorPair(node, "Legs", c->Colors.Legs); YAJL_CHECK(YAJLAddColorPair(g, "Legs", c->Colors.Legs));
AddColorPair(node, "Hair", c->Colors.Hair); YAJL_CHECK(YAJLAddColorPair(g, "Hair", c->Colors.Hair));
AddColorPair(node, "Feet", c->Colors.Feet); YAJL_CHECK(YAJLAddColorPair(g, "Feet", c->Colors.Feet));
AddColorPair(node, "Facehair", c->Colors.Facehair); YAJL_CHECK(YAJLAddColorPair(g, "Facehair", c->Colors.Facehair));
AddColorPair(node, "Hat", c->Colors.Hat); YAJL_CHECK(YAJLAddColorPair(g, "Hat", c->Colors.Hat));
AddColorPair(node, "Glasses", c->Colors.Glasses); YAJL_CHECK(YAJLAddColorPair(g, "Glasses", c->Colors.Glasses));
AddIntPair(node, "speed", (int)(c->speed * 256)); YAJL_CHECK(YAJLAddIntPair(g, "speed", (int)(c->speed * 256)));
json_insert_pair_into_object(node, "Gun", json_new_string(c->Gun->name)); YAJL_CHECK(YAJLAddStringPair(g, "Gun", c->Gun->name));
if (c->Melee != NULL) 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); YAJL_CHECK(YAJLAddIntPair(g, "maxHealth", c->maxHealth));
AddIntPair(node, "excessHealth", c->excessHealth); YAJL_CHECK(YAJLAddIntPair(g, "excessHealth", c->excessHealth));
AddIntPair(node, "flags", c->flags); YAJL_CHECK(YAJLAddIntPair(g, "flags", c->flags));
if (c->Drop != NULL) if (c->Drop != NULL)
{ {
json_insert_pair_into_object( YAJL_CHECK(YAJLAddStringPair(g, "Drop", c->Drop->Name));
node, "Drop", json_new_string(c->Drop->Name));
} }
AddIntPair(node, "probabilityToMove", c->bot->probabilityToMove); YAJL_CHECK(
AddIntPair(node, "probabilityToTrack", c->bot->probabilityToTrack); YAJLAddIntPair(g, "probabilityToMove", c->bot->probabilityToMove));
AddIntPair(node, "probabilityToShoot", c->bot->probabilityToShoot); YAJL_CHECK(
AddIntPair(node, "actionDelay", c->bot->actionDelay); YAJLAddIntPair(g, "probabilityToTrack", c->bot->probabilityToTrack));
json_insert_child(charNode, node); 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() 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]; char buf[CDOGS_PATH_MAX];
sprintf(buf, "%s/characters.json", path); sprintf(buf, "%s/characters.json", path);
if (!TrySaveJSONFile(root, buf)) if (!YAJLTrySaveJSONFile(g, buf))
{ {
res = false; res = false;
goto bail; goto bail;
} }
bail: bail:
json_free_value(&root); if (g)
{
yajl_gen_clear(g);
yajl_gen_free(g);
}
return res; return res;
} }
@@ -393,7 +428,7 @@ bool CharacterIsPrisoner(const CharacterStore *store, const Character *c)
void CharacterSetHeadPart(Character *c, const HeadPart hp, const char *name) void CharacterSetHeadPart(Character *c, const HeadPart hp, const char *name)
{ {
CFREE(c->HeadParts[hp]); CFREE(c->HeadParts[hp]);
c->HeadParts[hp] = NULL; c->HeadParts[hp] = NULL;
if (name) if (name)
{ {
CSTRDUP(c->HeadParts[hp], name); CSTRDUP(c->HeadParts[hp], name);
@@ -417,11 +452,11 @@ void CharacterShuffleAppearance(Character *c)
&gCharacterClasses.CustomClasses, &gCharacterClasses.CustomClasses,
charClass - gCharacterClasses.Classes.size); charClass - gCharacterClasses.Classes.size);
} }
for (HeadPart hp = HEAD_PART_HAIR; hp < HEAD_PART_COUNT; hp++) for (HeadPart hp = HEAD_PART_HAIR; hp < HEAD_PART_COUNT; hp++)
{ {
const char *name = NULL; const char *name = NULL;
if (RAND_INT(0, 3)==0) if (RAND_INT(0, 3) == 0)
{ {
const CArray *hpNames = &gPicManager.headPartNames[hp]; const CArray *hpNames = &gPicManager.headPartNames[hp];
name = *(char **)CArrayGet(hpNames, rand() % hpNames->size); name = *(char **)CArrayGet(hpNames, rand() % hpNames->size);

View File

@@ -25,6 +25,8 @@
*/ */
#include "yajl_utils.h" #include "yajl_utils.h"
#include "log.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -245,3 +247,32 @@ bail:
CFREE(pathCopy); CFREE(pathCopy);
return out; 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_gen.h"
#include "yajl/api/yajl_tree.h" #include "yajl/api/yajl_tree.h"
yajl_val YAJLReadFile(const char *filename); yajl_val YAJLReadFile(const char *filename);
yajl_gen_status YAJLAddIntPair(yajl_gen g, const char *name, const int number); 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 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 YAJLBool(bool *value, yajl_val node, const char *name);
void YAJLInt(int *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 YAJLDouble(double *value, yajl_val node, const char *name);
void YAJLVec2i(struct vec2i *value, yajl_val node, const char *name); void YAJLVec2i(struct vec2i *value, yajl_val node, const char *name);
#define YAJL_GET_VEC2I(v) svec2i(\ #define YAJL_GET_VEC2I(v) \
(int)YAJL_GET_INTEGER(YAJL_GET_ARRAY(v)->values[0]),\ svec2i( \
(int)YAJL_GET_INTEGER(YAJL_GET_ARRAY(v)->values[1])) (int)YAJL_GET_INTEGER(YAJL_GET_ARRAY(v)->values[0]), \
(int)YAJL_GET_INTEGER(YAJL_GET_ARRAY(v)->values[1]))
// remember to free // remember to free
void YAJLStr(char **value, yajl_val node, const char *name); void YAJLStr(char **value, yajl_val node, const char *name);
char *YAJLGetStr(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); BlitUpdateFromBuf(&gGraphicsDevice, gGraphicsDevice.screen);
WindowContextPostRender(&gGraphicsDevice.gameWindow); 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; fileChanged = false;
strcpy(lastFile, buf); strcpy(lastFile, buf);
sAutosaveIndex = 0; sAutosaveIndex = 0;