diff --git a/missions/Sand.cdogscpn/campaign.json b/missions/Sand.cdogscpn/campaign.json index bd01e4f1..21dc7790 100644 --- a/missions/Sand.cdogscpn/campaign.json +++ b/missions/Sand.cdogscpn/campaign.json @@ -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 } \ No newline at end of file diff --git a/missions/Sand.cdogscpn/characters.json b/missions/Sand.cdogscpn/characters.json index 5099b5d0..2ddd959c 100644 --- a/missions/Sand.cdogscpn/characters.json +++ b/missions/Sand.cdogscpn/characters.json @@ -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 - }] -} \ No newline at end of file + "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 + } + ] +} diff --git a/missions/Sand.cdogscpn/missions.json b/missions/Sand.cdogscpn/missions.json index f3495bff..952c5ae5 100644 --- a/missions/Sand.cdogscpn/missions.json +++ b/missions/Sand.cdogscpn/missions.json @@ -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, diff --git a/src/cdogs/character.c b/src/cdogs/character.c index def95f09..0f34211d 100644 --- a/src/cdogs/character.c +++ b/src/cdogs/character.c @@ -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); diff --git a/src/cdogs/yajl_utils.c b/src/cdogs/yajl_utils.c index 6865e8a7..cd5de564 100644 --- a/src/cdogs/yajl_utils.c +++ b/src/cdogs/yajl_utils.c @@ -25,6 +25,8 @@ */ #include "yajl_utils.h" +#include "log.h" + #include #include @@ -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; +} diff --git a/src/cdogs/yajl_utils.h b/src/cdogs/yajl_utils.h index 3a69600d..30254629 100644 --- a/src/cdogs/yajl_utils.h +++ b/src/cdogs/yajl_utils.h @@ -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); \ No newline at end of file diff --git a/src/cdogsed/cdogsed.c b/src/cdogsed/cdogsed.c index a3e07a9d..9d7596a5 100644 --- a/src/cdogsed/cdogsed.c +++ b/src/cdogsed/cdogsed.c @@ -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;