mirror of
https://github.com/cxong/cdogs-sdl.git
synced 2025-07-23 07:23:01 +02:00
Change ammo to a campaign-level option #495
Remove ammo as a config option Also rework some campaigns with ammo and skip weapon menu options
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"Version": 4,
|
||||
"Version": 15,
|
||||
"Title": "de_dust",
|
||||
"Author": "Cong",
|
||||
"Description": "",
|
||||
"Ammo": false,
|
||||
"WeaponPersist": false,
|
||||
"SkipWeaponMenu": true,
|
||||
"Missions": 1
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"Version": 13,
|
||||
"Characters": []
|
||||
}
|
File diff suppressed because one or more lines are too long
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"Version": 4,
|
||||
"Version": 15,
|
||||
"Title": "Doom",
|
||||
"Author": "Cong",
|
||||
"Description": "Stuck on Mars for insubordination, you are a marine who has just arrived at the dullest assignment in the system. They say your employer, UAC, are performing secret experiments to do with teleportation, but what could go wrong with that?",
|
||||
"Ammo": true,
|
||||
"WeaponPersist": false,
|
||||
"SkipWeaponMenu": true,
|
||||
"Missions": 1
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"Version": 13,
|
||||
"Characters": []
|
||||
}
|
File diff suppressed because one or more lines are too long
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"Version": 4,
|
||||
"Version": 15,
|
||||
"Title": "Map01",
|
||||
"Author": "Cong",
|
||||
"Description": "",
|
||||
"Ammo": false,
|
||||
"WeaponPersist": false,
|
||||
"SkipWeaponMenu": true,
|
||||
"Missions": 1
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"Version": 13,
|
||||
"Characters": []
|
||||
}
|
File diff suppressed because one or more lines are too long
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"Version": 4,
|
||||
"Version": 15,
|
||||
"Title": "Map 07: Dead Simple",
|
||||
"Author": "Cong",
|
||||
"Description": "A simple deathmatch arena",
|
||||
"Ammo": false,
|
||||
"WeaponPersist": false,
|
||||
"SkipWeaponMenu": true,
|
||||
"Missions": 1
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"Version": 13,
|
||||
"Characters": []
|
||||
}
|
@@ -5,12 +5,8 @@
|
||||
"Type": "Static",
|
||||
"Width": 24,
|
||||
"Height": 28,
|
||||
"WallStyle": 4,
|
||||
"FloorStyle": 5,
|
||||
"RoomStyle": 2,
|
||||
"ExitStyle": 0,
|
||||
"KeyStyle": 0,
|
||||
"DoorStyle": 0,
|
||||
"ExitStyle": "hazard",
|
||||
"KeyStyle": "office",
|
||||
"Objectives": [],
|
||||
"Enemies": [],
|
||||
"SpecialChars": [],
|
||||
@@ -18,105 +14,170 @@
|
||||
"EnemyDensity": 0,
|
||||
"Weapons": ["Marine Pistol"],
|
||||
"Song": "",
|
||||
"WallColor": 18,
|
||||
"FloorColor": 14,
|
||||
"RoomColor": 4,
|
||||
"AltColor": 0,
|
||||
"Tiles": "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1,1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1,1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1,1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1,1,0,0,0,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,0,1,1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,4,4,4,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,4,1,4,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,4,1,4,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,4,4,4,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1,1,0,0,0,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,0,1,1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1,1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1,1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1,1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
|
||||
"TileClasses": {
|
||||
"1": {
|
||||
"Name": "wall",
|
||||
"Type": "Wall",
|
||||
"Style": "stone",
|
||||
"Mask": "94802cff",
|
||||
"MaskAlt": "840000ff",
|
||||
"CanWalk": false,
|
||||
"IsOpaque": true,
|
||||
"Shootable": true,
|
||||
"IsRoom": false
|
||||
},
|
||||
"4": {
|
||||
"Name": "",
|
||||
"Type": "",
|
||||
"Style": "",
|
||||
"Mask": "ffffffff",
|
||||
"MaskAlt": "ffffffff",
|
||||
"CanWalk": false,
|
||||
"IsOpaque": false,
|
||||
"Shootable": false,
|
||||
"IsRoom": false
|
||||
},
|
||||
"3": {
|
||||
"Name": "tile",
|
||||
"Type": "Floor",
|
||||
"Style": "dirt",
|
||||
"Mask": "007000ff",
|
||||
"MaskAlt": "840000ff",
|
||||
"CanWalk": true,
|
||||
"IsOpaque": false,
|
||||
"Shootable": false,
|
||||
"IsRoom": true
|
||||
},
|
||||
"0": {
|
||||
"Name": "tile",
|
||||
"Type": "Floor",
|
||||
"Style": "smallsquare",
|
||||
"Mask": "484848ff",
|
||||
"MaskAlt": "840000ff",
|
||||
"CanWalk": true,
|
||||
"IsOpaque": false,
|
||||
"Shootable": false,
|
||||
"IsRoom": false
|
||||
}
|
||||
},
|
||||
"Tiles": ["1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
|
||||
"1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1",
|
||||
"1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1",
|
||||
"1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1",
|
||||
"1,0,0,0,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,4,4,4,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,4,1,4,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,4,1,4,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,4,4,4,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,0,1",
|
||||
"1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,0,0,0,3,3,3,3,3,0,0,0,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1",
|
||||
"1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1",
|
||||
"1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1",
|
||||
"1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1",
|
||||
"1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"],
|
||||
"Access": ["0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
|
||||
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"],
|
||||
"StaticItems": [{
|
||||
"MapObject": "Rockets spawner",
|
||||
"Positions": [[9,
|
||||
24],
|
||||
[15,
|
||||
24],
|
||||
[4,
|
||||
24],
|
||||
[4,
|
||||
3],
|
||||
[9,
|
||||
3],
|
||||
[15,
|
||||
3],
|
||||
[12,
|
||||
18],
|
||||
[12,
|
||||
9],
|
||||
[9,
|
||||
13],
|
||||
[15,
|
||||
13],
|
||||
[7,
|
||||
20],
|
||||
[15,
|
||||
15],
|
||||
[17,
|
||||
7]]
|
||||
"MapObject": "Rockets ammo spawner",
|
||||
"Positions": [[9, 24],
|
||||
[15, 24],
|
||||
[4, 24],
|
||||
[4, 3],
|
||||
[9, 3],
|
||||
[15, 3],
|
||||
[12, 18],
|
||||
[12, 9],
|
||||
[9, 13],
|
||||
[15, 13],
|
||||
[7, 20],
|
||||
[15, 15],
|
||||
[17, 7]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Shells spawner",
|
||||
"Positions": [[2,
|
||||
17],
|
||||
[2,
|
||||
10],
|
||||
[22,
|
||||
10],
|
||||
[22,
|
||||
17],
|
||||
[22,
|
||||
15]]
|
||||
"MapObject": "Shells ammo spawner",
|
||||
"Positions": [[2, 17],
|
||||
[2, 10],
|
||||
[22, 10],
|
||||
[22, 17],
|
||||
[22, 15]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Bullets spawner",
|
||||
"Positions": [[20,
|
||||
3],
|
||||
[20,
|
||||
24]]
|
||||
"MapObject": "Bullets ammo spawner",
|
||||
"Positions": [[20, 3],
|
||||
[20, 24]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Super shotgun spawner",
|
||||
"Positions": [[22,
|
||||
12]]
|
||||
"Positions": [[22, 12]]
|
||||
},
|
||||
{
|
||||
"MapObject": "health spawner",
|
||||
"Positions": [[9,
|
||||
18],
|
||||
[7,
|
||||
13]]
|
||||
"Positions": [[9, 18],
|
||||
[7, 13]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Chaingun spawner",
|
||||
"Positions": [[15,
|
||||
18]]
|
||||
"Positions": [[15, 18]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Mini cells spawner",
|
||||
"Positions": [[17,
|
||||
19],
|
||||
[7,
|
||||
7]]
|
||||
"MapObject": "Mini cells ammo spawner",
|
||||
"Positions": [[17, 19],
|
||||
[7, 7]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Plasma gun spawner",
|
||||
"Positions": [[15,
|
||||
9]]
|
||||
"Positions": [[15, 9]]
|
||||
},
|
||||
{
|
||||
"MapObject": "Launcher spawner",
|
||||
"Positions": [[9,
|
||||
9]]
|
||||
"Positions": [[9, 9]]
|
||||
}],
|
||||
"StaticWrecks": [],
|
||||
"StaticCharacters": [],
|
||||
"StaticObjectives": [],
|
||||
"StaticKeys": [],
|
||||
"Start": [0,
|
||||
0],
|
||||
"Start": [0, 0],
|
||||
"Exit": {
|
||||
"Start": [0,
|
||||
0],
|
||||
"End": [0,
|
||||
0]
|
||||
"Start": [0, 0],
|
||||
"End": [0, 0]
|
||||
}
|
||||
}]
|
||||
}
|
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"Version": 12,
|
||||
"Version": 15,
|
||||
"Title": "Temple of Carnage",
|
||||
"Author": "Sauer2",
|
||||
"Description": "A temple for the deities of massacre!",
|
||||
"Ammo": false,
|
||||
"WeaponPersist": false,
|
||||
"SkipWeaponMenu": true,
|
||||
"Missions": 1
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"Version": 12,
|
||||
"Version": 13,
|
||||
"Characters": []
|
||||
}
|
File diff suppressed because one or more lines are too long
@@ -3,6 +3,7 @@
|
||||
"Title": "Doom",
|
||||
"Author": "Cong",
|
||||
"Description": "Stuck on Mars for insubordination, you are a marine who has just arrived at the dullest assignment in the system. They say your employer, UAC, are performing secret experiments to do with teleportation, but what could go wrong with that?",
|
||||
"Ammo": true,
|
||||
"WeaponPersist": true,
|
||||
"SkipWeaponMenu": true,
|
||||
"Missions": 8
|
||||
|
@@ -717,7 +717,7 @@ static void FireWeapon(TActor *a, Weapon *w)
|
||||
}
|
||||
if (!ActorCanFireWeapon(a, w))
|
||||
{
|
||||
if (!WeaponIsLocked(w) && ConfigGetBool(&gConfig, "Game.Ammo"))
|
||||
if (!WeaponIsLocked(w) && gCampaign.Setting.Ammo)
|
||||
{
|
||||
CASSERT(
|
||||
ActorWeaponGetAmmo(a, w->Gun) == 0, "should be out of ammo");
|
||||
@@ -733,7 +733,7 @@ static void FireWeapon(TActor *a, Weapon *w)
|
||||
ActorFire(w, a);
|
||||
if (a->PlayerUID >= 0)
|
||||
{
|
||||
if (ConfigGetBool(&gConfig, "Game.Ammo") && w->Gun->AmmoId >= 0)
|
||||
if (gCampaign.Setting.Ammo && w->Gun->AmmoId >= 0)
|
||||
{
|
||||
GameEvent e = GameEventNew(GAME_EVENT_ACTOR_USE_AMMO);
|
||||
e.u.UseAmmo.UID = a->uid;
|
||||
@@ -1103,7 +1103,7 @@ static void ActorAddGunPickup(const TActor *actor);
|
||||
static void ActorDie(TActor *actor)
|
||||
{
|
||||
// Add an ammo pickup of the actor's gun
|
||||
if (ConfigGetBool(&gConfig, "Game.Ammo"))
|
||||
if (gCampaign.Setting.Ammo)
|
||||
{
|
||||
ActorAddAmmoPickup(actor);
|
||||
}
|
||||
@@ -1432,8 +1432,7 @@ static void ActorAddAmmoPickup(const TActor *actor)
|
||||
return false;
|
||||
}
|
||||
const bool hasAmmo = ActorWeaponGetAmmo(a, w->Gun) != 0;
|
||||
return !WeaponIsLocked(w) &&
|
||||
(!ConfigGetBool(&gConfig, "Game.Ammo") || hasAmmo);
|
||||
return !WeaponIsLocked(w) && (!gCampaign.Setting.Ammo || hasAmmo);
|
||||
}
|
||||
bool ActorTrySwitchWeapon(const TActor *a, const bool allGuns)
|
||||
{
|
||||
|
@@ -1,30 +1,30 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
|
||||
Copyright (c) 2013-2015, 2017-2018 Cong Xu
|
||||
All rights reserved.
|
||||
Copyright (c) 2013-2015, 2017-2018, 2020 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "ai_coop.h"
|
||||
|
||||
@@ -36,8 +36,7 @@
|
||||
#define CONFUSION_STATE_TICKS_MIN 25
|
||||
#define CONFUSION_STATE_TICKS_RANGE 25
|
||||
|
||||
#define DISTANCE_TO_KEEP_OUT_OF_WAY SQUARED(4*16)
|
||||
|
||||
#define DISTANCE_TO_KEEP_OUT_OF_WAY SQUARED(4 * 16)
|
||||
|
||||
static int AICoopGetCmdNormal(TActor *actor);
|
||||
int AICoopGetCmd(TActor *actor, const int ticks)
|
||||
@@ -64,9 +63,8 @@ int AICoopGetCmd(TActor *actor, const int ticks)
|
||||
actor->aiContext->Delay = MAX(0, actor->aiContext->Delay - ticks);
|
||||
if (actor->aiContext->Delay == 0)
|
||||
{
|
||||
actor->aiContext->Delay =
|
||||
CONFUSION_STATE_TICKS_MIN +
|
||||
(rand() % CONFUSION_STATE_TICKS_RANGE);
|
||||
actor->aiContext->Delay = CONFUSION_STATE_TICKS_MIN +
|
||||
(rand() % CONFUSION_STATE_TICKS_RANGE);
|
||||
if (s->Type == AI_CONFUSION_CONFUSED)
|
||||
{
|
||||
s->Type = AI_CONFUSION_CORRECT;
|
||||
@@ -76,9 +74,8 @@ int AICoopGetCmd(TActor *actor, const int ticks)
|
||||
ActorSetAIState(actor, AI_STATE_CONFUSED);
|
||||
s->Type = AI_CONFUSION_CONFUSED;
|
||||
// Generate the confused action
|
||||
s->Cmd = rand() &
|
||||
(CMD_LEFT | CMD_RIGHT | CMD_UP | CMD_DOWN |
|
||||
CMD_BUTTON1 | CMD_BUTTON2);
|
||||
s->Cmd = rand() & (CMD_LEFT | CMD_RIGHT | CMD_UP | CMD_DOWN |
|
||||
CMD_BUTTON1 | CMD_BUTTON2);
|
||||
}
|
||||
}
|
||||
// Choose confusion action based on state
|
||||
@@ -136,24 +133,25 @@ static int AICoopGetCmdNormal(TActor *actor)
|
||||
struct vec2i v;
|
||||
struct vec2 dangerBulletPos = svec2_zero();
|
||||
for (v.x = actorTilePos.x - 1;
|
||||
v.x <= actorTilePos.x + 1 && svec2_is_zero(dangerBulletPos);
|
||||
v.x++)
|
||||
v.x <= actorTilePos.x + 1 && svec2_is_zero(dangerBulletPos); v.x++)
|
||||
{
|
||||
for (v.y = actorTilePos.y - 1;
|
||||
v.y <= actorTilePos.y + 1 && svec2_is_zero(dangerBulletPos);
|
||||
v.y++)
|
||||
v.y <= actorTilePos.y + 1 && svec2_is_zero(dangerBulletPos);
|
||||
v.y++)
|
||||
{
|
||||
const Tile *t = MapGetTile(&gMap, v);
|
||||
if (t == NULL) continue;
|
||||
if (t == NULL)
|
||||
continue;
|
||||
CA_FOREACH(const ThingId, tid, t->things)
|
||||
// Only look for bullets
|
||||
if (tid->Kind != KIND_MOBILEOBJECT) continue;
|
||||
const TMobileObject *mo = CArrayGet(&gMobObjs, tid->Id);
|
||||
if (mo->bulletClass->HurtAlways)
|
||||
{
|
||||
dangerBulletPos = mo->thing.Pos;
|
||||
break;
|
||||
}
|
||||
// Only look for bullets
|
||||
if (tid->Kind != KIND_MOBILEOBJECT)
|
||||
continue;
|
||||
const TMobileObject *mo = CArrayGet(&gMobObjs, tid->Id);
|
||||
if (mo->bulletClass->HurtAlways)
|
||||
{
|
||||
dangerBulletPos = mo->thing.Pos;
|
||||
break;
|
||||
}
|
||||
CA_FOREACH_END()
|
||||
}
|
||||
}
|
||||
@@ -165,7 +163,7 @@ static int AICoopGetCmdNormal(TActor *actor)
|
||||
|
||||
// Check the weapon for ammo
|
||||
int lowAmmoGun = -1;
|
||||
if (ConfigGetBool(&gConfig, "Game.Ammo") && actor->aiContext->OnGunId == -1)
|
||||
if (gCampaign.Setting.Ammo && actor->aiContext->OnGunId == -1)
|
||||
{
|
||||
// Check all our weapons
|
||||
// Prefer guns using ammo
|
||||
@@ -213,12 +211,10 @@ static int AICoopGetCmdNormal(TActor *actor)
|
||||
// - if we have a free slot
|
||||
if (actor->aiContext->OnGunId != -1)
|
||||
{
|
||||
const WeaponClass *wc =
|
||||
IdWeaponClass(actor->aiContext->OnGunId);
|
||||
const WeaponClass *wc = IdWeaponClass(actor->aiContext->OnGunId);
|
||||
if ((lowAmmoGun != -1 && !ActorHasGun(actor, wc)) ||
|
||||
(wc->IsGrenade ?
|
||||
ActorGetNumGrenades(actor) < MAX_GRENADES :
|
||||
ActorGetNumGuns(actor) < MAX_GUNS))
|
||||
(wc->IsGrenade ? ActorGetNumGrenades(actor) < MAX_GRENADES
|
||||
: ActorGetNumGuns(actor) < MAX_GUNS))
|
||||
{
|
||||
actor->aiContext->OnGunId = -1;
|
||||
// Pick it up
|
||||
@@ -239,10 +235,10 @@ static int AICoopGetCmdNormal(TActor *actor)
|
||||
for (int uid = 0; uid < actor->PlayerUID; uid++)
|
||||
{
|
||||
const PlayerData *pd = PlayerDataGetByUID(uid);
|
||||
if (pd == NULL || !IsPlayerAlive(pd)) continue;
|
||||
if (pd == NULL || !IsPlayerAlive(pd))
|
||||
continue;
|
||||
const TActor *p = ActorGetByUID(pd->ActorUID);
|
||||
const float distance2 = svec2_distance_squared(
|
||||
actor->Pos, p->Pos);
|
||||
const float distance2 = svec2_distance_squared(actor->Pos, p->Pos);
|
||||
if (!closestPlayer || distance2 < minDistance2)
|
||||
{
|
||||
minDistance2 = distance2;
|
||||
@@ -265,7 +261,7 @@ static int AICoopGetCmdNormal(TActor *actor)
|
||||
{
|
||||
distanceTooFarFromPlayer = 2.0f;
|
||||
}
|
||||
if (closestPlayer && minDistance2 > SQUARED(distanceTooFarFromPlayer*16))
|
||||
if (closestPlayer && minDistance2 > SQUARED(distanceTooFarFromPlayer * 16))
|
||||
{
|
||||
ActorSetAIState(actor, AI_STATE_FOLLOW);
|
||||
return SmartGoto(actor, closestPlayer->Pos, minDistance2);
|
||||
@@ -276,8 +272,8 @@ static int AICoopGetCmdNormal(TActor *actor)
|
||||
if (closestEnemy)
|
||||
{
|
||||
const float minEnemyDistance = CHEBYSHEV_DISTANCE(
|
||||
actor->Pos.x, actor->Pos.y,
|
||||
closestEnemy->Pos.x, closestEnemy->Pos.y);
|
||||
actor->Pos.x, actor->Pos.y, closestEnemy->Pos.x,
|
||||
closestEnemy->Pos.y);
|
||||
// Also only engage if there's a clear shot
|
||||
if (minEnemyDistance > 0 && minEnemyDistance < 12 * 16 &&
|
||||
AIHasClearShot(actor->Pos, closestEnemy->Pos))
|
||||
@@ -301,7 +297,7 @@ static int AICoopGetCmdNormal(TActor *actor)
|
||||
// Look for objectives nearby to complete
|
||||
int cmd;
|
||||
if (TryCompleteNearbyObjective(
|
||||
actor, closestPlayer, distanceTooFarFromPlayer, &cmd))
|
||||
actor, closestPlayer, distanceTooFarFromPlayer, &cmd))
|
||||
{
|
||||
return cmd;
|
||||
}
|
||||
@@ -310,15 +306,14 @@ static int AICoopGetCmdNormal(TActor *actor)
|
||||
// run into them, but keep away if we're too close
|
||||
if (closestPlayer)
|
||||
{
|
||||
if (minDistance2 > SQUARED(2*16))
|
||||
if (minDistance2 > SQUARED(2 * 16))
|
||||
{
|
||||
ActorSetAIState(actor, AI_STATE_FOLLOW);
|
||||
return SmartGoto(actor, closestPlayer->Pos, minDistance2);
|
||||
}
|
||||
else if (minDistance2 < SQUARED(4*16/3))
|
||||
else if (minDistance2 < SQUARED(4 * 16 / 3))
|
||||
{
|
||||
return
|
||||
CmdGetReverse(AIGotoDirect(actor->Pos, closestPlayer->Pos));
|
||||
return CmdGetReverse(AIGotoDirect(actor->Pos, closestPlayer->Pos));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,8 +379,7 @@ static int SmartGoto(
|
||||
typedef struct
|
||||
{
|
||||
float Distance2;
|
||||
union
|
||||
{
|
||||
union {
|
||||
const Objective *Objective;
|
||||
int UID;
|
||||
} u;
|
||||
@@ -422,20 +416,18 @@ static bool TryCompleteNearbyObjective(
|
||||
hasNoUpdates =
|
||||
objState->u.Obj && objState->LastDone == objState->u.Obj->done;
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_KILL:
|
||||
{
|
||||
const TActor *target = ActorGetByUID(objState->u.UID);
|
||||
hasNoUpdates = target->health > 0;
|
||||
// Update target position
|
||||
objState->Goal = target->thing.Pos;
|
||||
}
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_PICKUP:
|
||||
{
|
||||
const Pickup *p = PickupGetByUID(objState->u.UID);
|
||||
hasNoUpdates = p != NULL && p->isInUse;
|
||||
}
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_KILL: {
|
||||
const TActor *target = ActorGetByUID(objState->u.UID);
|
||||
hasNoUpdates = target->health > 0;
|
||||
// Update target position
|
||||
objState->Goal = target->thing.Pos;
|
||||
}
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_PICKUP: {
|
||||
const Pickup *p = PickupGetByUID(objState->u.UID);
|
||||
hasNoUpdates = p != NULL && p->isInUse;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
@@ -458,8 +450,9 @@ static bool TryCompleteNearbyObjective(
|
||||
// First, check if mission complete;
|
||||
// if so (and there's a path) go to exit
|
||||
const struct vec2 exitPos = MapGetExitPos(&gMap);
|
||||
if (CanCompleteMission(&gMission) && CanGetObjective(
|
||||
exitPos, actor->Pos, closestPlayer, distanceTooFarFromPlayer))
|
||||
if (CanCompleteMission(&gMission) &&
|
||||
CanGetObjective(
|
||||
exitPos, actor->Pos, closestPlayer, distanceTooFarFromPlayer))
|
||||
{
|
||||
ActorSetAIState(actor, AI_STATE_NEXT_OBJECTIVE);
|
||||
objState->Type = AI_OBJECTIVE_TYPE_EXIT;
|
||||
@@ -475,41 +468,41 @@ static bool TryCompleteNearbyObjective(
|
||||
|
||||
// Starting from the closest objectives, find one we can go to
|
||||
CA_FOREACH(ClosestObjective, c, objectives)
|
||||
if (CanGetObjective(
|
||||
if (CanGetObjective(
|
||||
c->Pos, actor->Pos, closestPlayer, distanceTooFarFromPlayer))
|
||||
{
|
||||
ActorSetAIState(actor, AI_STATE_NEXT_OBJECTIVE);
|
||||
objState->Type = c->Type;
|
||||
objState->IsDestructible = c->IsDestructible;
|
||||
switch (c->Type)
|
||||
{
|
||||
ActorSetAIState(actor, AI_STATE_NEXT_OBJECTIVE);
|
||||
objState->Type = c->Type;
|
||||
objState->IsDestructible = c->IsDestructible;
|
||||
switch (c->Type)
|
||||
{
|
||||
case AI_OBJECTIVE_TYPE_KEY:
|
||||
objState->LastDone = KeycardCount(gMission.KeyFlags);
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_NORMAL:
|
||||
objState->u.Obj = c->u.Objective;
|
||||
objState->LastDone = c->u.Objective->done;
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_KILL:
|
||||
objState->u.UID = c->u.UID;
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_PICKUP:
|
||||
objState->u.UID = c->u.UID;
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
objState->Goal = c->Pos;
|
||||
*cmdOut = GotoObjective(actor, c->Distance2);
|
||||
return true;
|
||||
case AI_OBJECTIVE_TYPE_KEY:
|
||||
objState->LastDone = KeycardCount(gMission.KeyFlags);
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_NORMAL:
|
||||
objState->u.Obj = c->u.Objective;
|
||||
objState->LastDone = c->u.Objective->done;
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_KILL:
|
||||
objState->u.UID = c->u.UID;
|
||||
break;
|
||||
case AI_OBJECTIVE_TYPE_PICKUP:
|
||||
objState->u.UID = c->u.UID;
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
break;
|
||||
}
|
||||
objState->Goal = c->Pos;
|
||||
*cmdOut = GotoObjective(actor, c->Distance2);
|
||||
return true;
|
||||
}
|
||||
CA_FOREACH_END()
|
||||
return false;
|
||||
}
|
||||
static bool OnClosestPickupGun(
|
||||
ClosestObjective *co, const Pickup *p,
|
||||
const TActor *actor, const TActor *closestPlayer);
|
||||
ClosestObjective *co, const Pickup *p, const TActor *actor,
|
||||
const TActor *closestPlayer);
|
||||
static int CompareClosestObjective(const void *v1, const void *v2);
|
||||
static void FindObjectivesSortedByDistance(
|
||||
CArray *objectives, const TActor *actor, const TActor *closestPlayer)
|
||||
@@ -519,8 +512,7 @@ static void FindObjectivesSortedByDistance(
|
||||
// If PVP, find the closest enemy and go to them
|
||||
if (IsPVP(gCampaign.Entry.Mode))
|
||||
{
|
||||
const TActor *closestEnemy =
|
||||
AIGetClosestVisibleEnemy(actor, true);
|
||||
const TActor *closestEnemy = AIGetClosestVisibleEnemy(actor, true);
|
||||
if (closestEnemy != NULL)
|
||||
{
|
||||
ClosestObjective co;
|
||||
@@ -536,170 +528,167 @@ static void FindObjectivesSortedByDistance(
|
||||
|
||||
// Look for pickups
|
||||
CA_FOREACH(const Pickup, p, gPickups)
|
||||
if (!p->isInUse)
|
||||
if (!p->isInUse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ClosestObjective co;
|
||||
memset(&co, 0, sizeof co);
|
||||
co.Pos = p->thing.Pos;
|
||||
co.IsDestructible = false;
|
||||
co.Type = AI_OBJECTIVE_TYPE_NORMAL;
|
||||
switch (p->class->Type)
|
||||
{
|
||||
case PICKUP_KEYCARD:
|
||||
co.Type = AI_OBJECTIVE_TYPE_KEY;
|
||||
break;
|
||||
case PICKUP_JEWEL:
|
||||
break;
|
||||
case PICKUP_HEALTH:
|
||||
// Pick up if we are on low health, and lower than lead player
|
||||
if (actor->health > ModeMaxHealth(gCampaign.Entry.Mode) / 4)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ClosestObjective co;
|
||||
memset(&co, 0, sizeof co);
|
||||
co.Pos = p->thing.Pos;
|
||||
co.IsDestructible = false;
|
||||
co.Type = AI_OBJECTIVE_TYPE_NORMAL;
|
||||
switch (p->class->Type)
|
||||
{
|
||||
case PICKUP_KEYCARD:
|
||||
co.Type = AI_OBJECTIVE_TYPE_KEY;
|
||||
break;
|
||||
case PICKUP_JEWEL:
|
||||
break;
|
||||
case PICKUP_HEALTH:
|
||||
// Pick up if we are on low health, and lower than lead player
|
||||
if (actor->health > ModeMaxHealth(gCampaign.Entry.Mode) / 4)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
co.Type = AI_OBJECTIVE_TYPE_PICKUP;
|
||||
co.u.UID = p->UID;
|
||||
break;
|
||||
case PICKUP_AMMO:
|
||||
{
|
||||
// Pick up if we use this ammo, have less ammo than starting,
|
||||
// and lower than lead player, who uses the ammo
|
||||
const int ammoId = p->class->u.Ammo.Id;
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, ammoId);
|
||||
const int ammoAmount = *(int *)CArrayGet(&actor->ammo, ammoId);
|
||||
if (!ActorUsesAmmo(actor, ammoId) ||
|
||||
ammoAmount > ammo->Amount ||
|
||||
(closestPlayer != NULL &&
|
||||
ActorUsesAmmo(closestPlayer, ammoId) &&
|
||||
ammoAmount > *(int *)CArrayGet(&closestPlayer->ammo, ammoId)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
co.Type = AI_OBJECTIVE_TYPE_PICKUP;
|
||||
co.u.UID = p->UID;
|
||||
}
|
||||
break;
|
||||
case PICKUP_GUN:
|
||||
if (!OnClosestPickupGun(&co, p, actor, closestPlayer))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Not something we want to pick up
|
||||
continue;
|
||||
}
|
||||
// Check if the pickup is actually accessible
|
||||
// This is because random spawning may cause some pickups to be spawned
|
||||
// in inaccessible areas
|
||||
if (!TileCanWalk(MapGetTile(&gMap, Vec2ToTile(co.Pos))))
|
||||
co.Type = AI_OBJECTIVE_TYPE_PICKUP;
|
||||
co.u.UID = p->UID;
|
||||
break;
|
||||
case PICKUP_AMMO: {
|
||||
// Pick up if we use this ammo, have less ammo than starting,
|
||||
// and lower than lead player, who uses the ammo
|
||||
const int ammoId = p->class->u.Ammo.Id;
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, ammoId);
|
||||
const int ammoAmount = *(int *)CArrayGet(&actor->ammo, ammoId);
|
||||
if (!ActorUsesAmmo(actor, ammoId) || ammoAmount > ammo->Amount ||
|
||||
(closestPlayer != NULL && ActorUsesAmmo(closestPlayer, ammoId) &&
|
||||
ammoAmount > *(int *)CArrayGet(&closestPlayer->ammo, ammoId)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
co.Distance2 = svec2_distance_squared(actor->Pos, co.Pos);
|
||||
if (co.Type == AI_OBJECTIVE_TYPE_NORMAL)
|
||||
co.Type = AI_OBJECTIVE_TYPE_PICKUP;
|
||||
co.u.UID = p->UID;
|
||||
}
|
||||
break;
|
||||
case PICKUP_GUN:
|
||||
if (!OnClosestPickupGun(&co, p, actor, closestPlayer))
|
||||
{
|
||||
const int objective = ObjectiveFromThing(p->thing.flags);
|
||||
co.u.Objective =
|
||||
CArrayGet(&gMission.missionData->Objectives, objective);
|
||||
continue;
|
||||
}
|
||||
CArrayPushBack(objectives, &co);
|
||||
break;
|
||||
default:
|
||||
// Not something we want to pick up
|
||||
continue;
|
||||
}
|
||||
// Check if the pickup is actually accessible
|
||||
// This is because random spawning may cause some pickups to be spawned
|
||||
// in inaccessible areas
|
||||
if (!TileCanWalk(MapGetTile(&gMap, Vec2ToTile(co.Pos))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
co.Distance2 = svec2_distance_squared(actor->Pos, co.Pos);
|
||||
if (co.Type == AI_OBJECTIVE_TYPE_NORMAL)
|
||||
{
|
||||
const int objective = ObjectiveFromThing(p->thing.flags);
|
||||
co.u.Objective =
|
||||
CArrayGet(&gMission.missionData->Objectives, objective);
|
||||
}
|
||||
CArrayPushBack(objectives, &co);
|
||||
CA_FOREACH_END()
|
||||
|
||||
// Look for destructibles
|
||||
CA_FOREACH(const TObject, o, gObjs)
|
||||
if (!o->isInUse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ClosestObjective co;
|
||||
memset(&co, 0, sizeof co);
|
||||
co.Pos = o->thing.Pos;
|
||||
co.IsDestructible = true;
|
||||
co.Type = AI_OBJECTIVE_TYPE_NORMAL;
|
||||
if (!(o->thing.flags & THING_OBJECTIVE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Destructible objective; go towards it and fire
|
||||
co.Distance2 = svec2_distance_squared(actor->Pos, co.Pos);
|
||||
if (co.Type == AI_OBJECTIVE_TYPE_NORMAL)
|
||||
{
|
||||
const int objective = ObjectiveFromThing(o->thing.flags);
|
||||
co.u.Objective =
|
||||
CArrayGet(&gMission.missionData->Objectives, objective);
|
||||
}
|
||||
CArrayPushBack(objectives, &co);
|
||||
if (!o->isInUse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ClosestObjective co;
|
||||
memset(&co, 0, sizeof co);
|
||||
co.Pos = o->thing.Pos;
|
||||
co.IsDestructible = true;
|
||||
co.Type = AI_OBJECTIVE_TYPE_NORMAL;
|
||||
if (!(o->thing.flags & THING_OBJECTIVE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Destructible objective; go towards it and fire
|
||||
co.Distance2 = svec2_distance_squared(actor->Pos, co.Pos);
|
||||
if (co.Type == AI_OBJECTIVE_TYPE_NORMAL)
|
||||
{
|
||||
const int objective = ObjectiveFromThing(o->thing.flags);
|
||||
co.u.Objective =
|
||||
CArrayGet(&gMission.missionData->Objectives, objective);
|
||||
}
|
||||
CArrayPushBack(objectives, &co);
|
||||
CA_FOREACH_END()
|
||||
|
||||
// Look for kill or rescue objectives
|
||||
CA_FOREACH(const TActor, a, gActors)
|
||||
if (!a->isInUse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const Thing *ti = &a->thing;
|
||||
if (!(ti->flags & THING_OBJECTIVE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const int objective = ObjectiveFromThing(ti->flags);
|
||||
const Objective *o =
|
||||
CArrayGet(&gMission.missionData->Objectives, objective);
|
||||
if (o->Type != OBJECTIVE_KILL && o->Type != OBJECTIVE_RESCUE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Only rescue those that need to be rescued
|
||||
if (o->Type == OBJECTIVE_RESCUE && !(a->flags & FLAGS_PRISONER))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ClosestObjective co;
|
||||
memset(&co, 0, sizeof co);
|
||||
co.Pos = ti->Pos;
|
||||
co.IsDestructible = false;
|
||||
co.Type = AI_OBJECTIVE_TYPE_NORMAL;
|
||||
co.Distance2 = svec2_distance_squared(actor->Pos, co.Pos);
|
||||
co.u.Objective = o;
|
||||
CArrayPushBack(objectives, &co);
|
||||
if (!a->isInUse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const Thing *ti = &a->thing;
|
||||
if (!(ti->flags & THING_OBJECTIVE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const int objective = ObjectiveFromThing(ti->flags);
|
||||
const Objective *o =
|
||||
CArrayGet(&gMission.missionData->Objectives, objective);
|
||||
if (o->Type != OBJECTIVE_KILL && o->Type != OBJECTIVE_RESCUE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Only rescue those that need to be rescued
|
||||
if (o->Type == OBJECTIVE_RESCUE && !(a->flags & FLAGS_PRISONER))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ClosestObjective co;
|
||||
memset(&co, 0, sizeof co);
|
||||
co.Pos = ti->Pos;
|
||||
co.IsDestructible = false;
|
||||
co.Type = AI_OBJECTIVE_TYPE_NORMAL;
|
||||
co.Distance2 = svec2_distance_squared(actor->Pos, co.Pos);
|
||||
co.u.Objective = o;
|
||||
CArrayPushBack(objectives, &co);
|
||||
CA_FOREACH_END()
|
||||
|
||||
// Look for explore objectives
|
||||
CA_FOREACH(const Objective, o, gMission.missionData->Objectives)
|
||||
if (o->Type != OBJECTIVE_INVESTIGATE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (ObjectiveIsComplete(o) || MapGetExploredPercentage(&gMap) == 100)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Find the nearest unexplored tile
|
||||
const struct vec2i actorTile = Vec2ToTile(actor->Pos);
|
||||
const struct vec2i unexploredTile = MapSearchTileAround(
|
||||
&gMap, actorTile, MapTileIsUnexplored);
|
||||
ClosestObjective co;
|
||||
memset(&co, 0, sizeof co);
|
||||
co.Pos = Vec2CenterOfTile(unexploredTile);
|
||||
co.IsDestructible = false;
|
||||
co.Type = AI_OBJECTIVE_TYPE_NORMAL;
|
||||
co.Distance2 = svec2_distance_squared(actor->Pos, co.Pos);
|
||||
co.u.Objective = o;
|
||||
CArrayPushBack(objectives, &co);
|
||||
if (o->Type != OBJECTIVE_INVESTIGATE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (ObjectiveIsComplete(o) || MapGetExploredPercentage(&gMap) == 100)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Find the nearest unexplored tile
|
||||
const struct vec2i actorTile = Vec2ToTile(actor->Pos);
|
||||
const struct vec2i unexploredTile =
|
||||
MapSearchTileAround(&gMap, actorTile, MapTileIsUnexplored);
|
||||
ClosestObjective co;
|
||||
memset(&co, 0, sizeof co);
|
||||
co.Pos = Vec2CenterOfTile(unexploredTile);
|
||||
co.IsDestructible = false;
|
||||
co.Type = AI_OBJECTIVE_TYPE_NORMAL;
|
||||
co.Distance2 = svec2_distance_squared(actor->Pos, co.Pos);
|
||||
co.u.Objective = o;
|
||||
CArrayPushBack(objectives, &co);
|
||||
CA_FOREACH_END()
|
||||
|
||||
// Sort according to distance
|
||||
qsort(
|
||||
objectives->data,
|
||||
objectives->size, objectives->elemSize, CompareClosestObjective);
|
||||
objectives->data, objectives->size, objectives->elemSize,
|
||||
CompareClosestObjective);
|
||||
}
|
||||
static bool OnClosestPickupGun(
|
||||
ClosestObjective *co, const Pickup *p,
|
||||
const TActor *actor, const TActor *closestPlayer)
|
||||
ClosestObjective *co, const Pickup *p, const TActor *actor,
|
||||
const TActor *closestPlayer)
|
||||
{
|
||||
if (!ConfigGetBool(&gConfig, "Game.Ammo"))
|
||||
if (!gCampaign.Setting.Ammo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -727,9 +716,8 @@ static bool OnClosestPickupGun(
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, ammoId);
|
||||
const int ammoAmount = *(int *)CArrayGet(&actor->ammo, ammoId);
|
||||
if (ammoAmount >= ammo->Amount ||
|
||||
(closestPlayer != NULL &&
|
||||
ActorUsesAmmo(closestPlayer, ammoId) &&
|
||||
ammoAmount > *(int *)CArrayGet(&closestPlayer->ammo, ammoId)))
|
||||
(closestPlayer != NULL && ActorUsesAmmo(closestPlayer, ammoId) &&
|
||||
ammoAmount > *(int *)CArrayGet(&closestPlayer->ammo, ammoId)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -795,8 +783,7 @@ static bool IsPosCloseEnoughToPlayer(
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const float distanceFromPlayer2 =
|
||||
svec2_distance_squared(pos, player->Pos);
|
||||
const float distanceFromPlayer2 = svec2_distance_squared(pos, player->Pos);
|
||||
const float distanceMax2 = SQUARED(distanceTooFarFromPlayer * 16);
|
||||
return distanceFromPlayer2 < distanceMax2;
|
||||
}
|
||||
@@ -822,7 +809,7 @@ static int GotoObjective(TActor *actor, const float objDistance2)
|
||||
cmd = AIHunt(actor, goal);
|
||||
if (AIHasClearShot(actor->Pos, goal))
|
||||
{
|
||||
cmd |= CMD_BUTTON1;
|
||||
cmd |= CMD_BUTTON1;
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
@@ -854,7 +841,7 @@ void AICoopSelectWeapons(
|
||||
gunCount++;
|
||||
}
|
||||
|
||||
if (ConfigGetBool(&gConfig, "Game.Ammo"))
|
||||
if (gCampaign.Setting.Ammo)
|
||||
{
|
||||
// Select pistol as an infinite-ammo backup
|
||||
const WeaponClass *pistol = StrWeaponClass("Pistol");
|
||||
|
@@ -53,6 +53,7 @@ typedef struct
|
||||
char *Title;
|
||||
char *Author;
|
||||
char *Description;
|
||||
bool Ammo;
|
||||
bool WeaponPersist;
|
||||
bool SkipWeaponMenu;
|
||||
CArray Missions; // of Mission
|
||||
|
@@ -579,7 +579,6 @@ Config ConfigDefault(void)
|
||||
ConfigGroupAdd(&game,
|
||||
ConfigNewInt("Lives", 2, 1, 5, 1, NULL, NULL));
|
||||
ConfigGroupAdd(&game, ConfigNewBool("HealthPickups", true));
|
||||
ConfigGroupAdd(&game, ConfigNewBool("Ammo", false));
|
||||
ConfigGroupAdd(&game, ConfigNewBool("Fog", true));
|
||||
ConfigGroupAdd(&game,
|
||||
ConfigNewInt("SightRange", 15, 8, 40, 1, NULL, NULL));
|
||||
|
@@ -1,29 +1,29 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (c) 2013-2017, 2019 Cong Xu
|
||||
All rights reserved.
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (c) 2013-2017, 2019-2020 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "player_hud.h"
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
#include "automap.h"
|
||||
#include "draw/draw_actor.h"
|
||||
#include "draw/nine_slice.h"
|
||||
#include "hud_defs.h"
|
||||
#include "hud/gauge.h"
|
||||
#include "hud_defs.h"
|
||||
|
||||
#define SCORE_WIDTH 26
|
||||
#define GRENADES_WIDTH 30
|
||||
@@ -41,7 +41,6 @@
|
||||
#define PLAYER_ICON_WIDTH 20
|
||||
#define BAR_HEIGHT 13
|
||||
|
||||
|
||||
void HUDPlayerInit(HUDPlayer *h)
|
||||
{
|
||||
memset(h, 0, sizeof *h);
|
||||
@@ -51,15 +50,16 @@ void HUDPlayerInit(HUDPlayer *h)
|
||||
void HUDPlayerUpdate(HUDPlayer *h, const PlayerData *p, const int ms)
|
||||
{
|
||||
const TActor *a = ActorGetByUID(p->ActorUID);
|
||||
if (a == NULL) return;
|
||||
if (a == NULL)
|
||||
return;
|
||||
|
||||
// Health
|
||||
HealthGaugeUpdate(&h->healthGauge, a, ms);
|
||||
}
|
||||
|
||||
static void DrawPlayerStatus(
|
||||
HUD *hud, const PlayerData *data, TActor *p,
|
||||
const int flags, const HUDPlayer *h, const Rect2i r);
|
||||
HUD *hud, const PlayerData *data, TActor *p, const int flags,
|
||||
const HUDPlayer *h, const Rect2i r);
|
||||
static void DrawPlayerObjectiveCompass(
|
||||
const HUD *hud, TActor *a, const int hudPlayerIndex, const int numViews);
|
||||
void DrawPlayerHUD(
|
||||
@@ -67,7 +67,8 @@ void DrawPlayerHUD(
|
||||
const int hudPlayerIndex, const Rect2i r, const int numViews)
|
||||
{
|
||||
TActor *a = IsPlayerAlive(p) ? ActorGetByUID(p->ActorUID) : NULL;
|
||||
DrawPlayerStatus(hud, p, a, drawFlags, &hud->hudPlayers[hudPlayerIndex], r);
|
||||
DrawPlayerStatus(
|
||||
hud, p, a, drawFlags, &hud->hudPlayers[hudPlayerIndex], r);
|
||||
HUDNumPopupsDrawPlayer(&hud->numPopups, hudPlayerIndex, drawFlags, r);
|
||||
DrawPlayerObjectiveCompass(hud, a, hudPlayerIndex, numViews);
|
||||
}
|
||||
@@ -89,15 +90,15 @@ static void DrawGunIcons(
|
||||
static void DrawGrenadeStatus(
|
||||
GraphicsDevice *g, const TActor *a, const int flags, const Rect2i r);
|
||||
static void DrawRadar(
|
||||
GraphicsDevice *device, const TActor *p,
|
||||
const int flags, const bool showExit);
|
||||
GraphicsDevice *device, const TActor *p, const int flags,
|
||||
const bool showExit);
|
||||
static void DrawHealth(
|
||||
GraphicsDevice *g, const TActor *a,
|
||||
const int flags, const HUDPlayer *h, const Rect2i r, const color_t mask);
|
||||
GraphicsDevice *g, const TActor *a, const int flags, const HUDPlayer *h,
|
||||
const Rect2i r, const color_t mask);
|
||||
// Draw player's score, health etc.
|
||||
static void DrawPlayerStatus(
|
||||
HUD *hud, const PlayerData *data, TActor *p,
|
||||
const int flags, const HUDPlayer *h, const Rect2i r)
|
||||
HUD *hud, const PlayerData *data, TActor *p, const int flags,
|
||||
const HUDPlayer *h, const Rect2i r)
|
||||
{
|
||||
const color_t mask = data->Char.Colors.Body;
|
||||
SDL_RendererFlip flip = SDL_FLIP_NONE;
|
||||
@@ -192,15 +193,15 @@ static void DrawPlayerIcon(
|
||||
}
|
||||
if (flags & HUDFLAGS_PLACE_BOTTOM)
|
||||
{
|
||||
pos.y = g->cachedConfig.Res.y - pos.y + 2;
|
||||
pos.y = g->cachedConfig.Res.y - pos.y + 2;
|
||||
}
|
||||
DrawActorPics(
|
||||
&pics, svec2i_add(pos, offset),
|
||||
Rect2iNew(svec2i(-12, -12), svec2i(12, 5)));
|
||||
}
|
||||
PicRender(
|
||||
framePic, g->gameWindow.renderer, picPos, mask, 0,
|
||||
svec2_one(), flip, Rect2iZero());
|
||||
framePic, g->gameWindow.renderer, picPos, mask, 0, svec2_one(), flip,
|
||||
Rect2iZero());
|
||||
}
|
||||
static void DrawScore(
|
||||
GraphicsDevice *g, const PicManager *pm, const TActor *a, const int score,
|
||||
@@ -227,7 +228,7 @@ static void DrawScore(
|
||||
char s[50];
|
||||
if (IsScoreNeeded(gCampaign.Entry.Mode))
|
||||
{
|
||||
if (ConfigGetBool(&gConfig, "Game.Ammo"))
|
||||
if (gCampaign.Setting.Ammo)
|
||||
{
|
||||
// Display money instead of ammo
|
||||
sprintf(s, "$%d", score);
|
||||
@@ -293,8 +294,8 @@ static void DrawWeaponStatus(
|
||||
}
|
||||
|
||||
Draw9Slice(
|
||||
g, backPic, Rect2iNew(pos, backPicSize), 0, 2, 0, 4, false,
|
||||
mask, SDL_FLIP_NONE);
|
||||
g, backPic, Rect2iNew(pos, backPicSize), 0, 2, 0, 4, false, mask,
|
||||
SDL_FLIP_NONE);
|
||||
|
||||
if (actor == NULL)
|
||||
{
|
||||
@@ -303,9 +304,10 @@ static void DrawWeaponStatus(
|
||||
|
||||
const Weapon *weapon = ACTOR_GET_WEAPON(actor);
|
||||
const WeaponClass *wc = weapon->Gun;
|
||||
CASSERT(wc != NULL, "weapon has no class");
|
||||
|
||||
// Draw gauge and ammo counter if ammo used
|
||||
if (ConfigGetBool(&gConfig, "Game.Ammo") && wc->AmmoId >= 0)
|
||||
if (gCampaign.Setting.Ammo && wc->AmmoId >= 0)
|
||||
{
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, wc->AmmoId);
|
||||
const int amount = ActorWeaponGetAmmo(actor, wc);
|
||||
@@ -318,10 +320,11 @@ static void DrawWeaponStatus(
|
||||
{
|
||||
const Pic *fillPic = PicManagerGetPic(pm, "hud/gauge_small_inner");
|
||||
const struct vec2i fillPicSize = svec2i(
|
||||
MAX(1, (AMMO_WIDTH - 1) * amount / ammo->Max), fillPic->size.y);
|
||||
MAX(1, (AMMO_WIDTH - 1) * amount / ammo->Max),
|
||||
fillPic->size.y);
|
||||
Draw9Slice(
|
||||
g, fillPic, Rect2iNew(svec2i(pos.x, pos.y), fillPicSize),
|
||||
1, 1, 1, 1, false, colorBlue, SDL_FLIP_NONE);
|
||||
g, fillPic, Rect2iNew(svec2i(pos.x, pos.y), fillPicSize), 1, 1,
|
||||
1, 1, false, colorBlue, SDL_FLIP_NONE);
|
||||
}
|
||||
|
||||
// Include ammo counter
|
||||
@@ -376,6 +379,7 @@ static void DrawGunIcons(
|
||||
|
||||
const Weapon *weapon = ACTOR_GET_WEAPON(actor);
|
||||
const WeaponClass *wc = weapon->Gun;
|
||||
CASSERT(wc != NULL, "weapon has no class");
|
||||
|
||||
// Aligned right
|
||||
const int right =
|
||||
@@ -391,7 +395,7 @@ static void DrawGunIcons(
|
||||
}
|
||||
|
||||
// Ammo icon
|
||||
if (ConfigGetBool(&gConfig, "Game.Ammo") && wc->AmmoId >= 0)
|
||||
if (gCampaign.Setting.Ammo && wc->AmmoId >= 0)
|
||||
{
|
||||
const Ammo *ammo = AmmoGetById(&gAmmo, wc->AmmoId);
|
||||
const struct vec2i ammoPos = svec2i_add(pos, svec2i(6, 5));
|
||||
@@ -405,8 +409,8 @@ static void DrawGunIcons(
|
||||
}
|
||||
|
||||
static void DrawGrenadeIcons(
|
||||
GraphicsDevice *g, const Pic *icon, const struct vec2i pos, const int width,
|
||||
const int amount);
|
||||
GraphicsDevice *g, const Pic *icon, const struct vec2i pos,
|
||||
const int width, const int amount);
|
||||
static void DrawGrenadeStatus(
|
||||
GraphicsDevice *g, const TActor *a, const int flags, const Rect2i r)
|
||||
{
|
||||
@@ -436,8 +440,7 @@ static void DrawGrenadeStatus(
|
||||
|
||||
// Draw number of grenade icons; if there are too many draw one with the
|
||||
// amount as text
|
||||
const bool useAmmo =
|
||||
ConfigGetBool(&gConfig, "Game.Ammo") && wc->AmmoId >= 0;
|
||||
const bool useAmmo = gCampaign.Setting.Ammo && wc->AmmoId >= 0;
|
||||
const int amount = useAmmo ? ActorWeaponGetAmmo(a, wc) : -1;
|
||||
const Pic *icon = WeaponClassGetIcon(wc);
|
||||
if (useAmmo && amount > 0 && amount <= MAX_GRENADE_ICONS)
|
||||
@@ -462,8 +465,8 @@ static void DrawGrenadeStatus(
|
||||
}
|
||||
}
|
||||
static void DrawGrenadeIcons(
|
||||
GraphicsDevice *g, const Pic *icon, const struct vec2i pos, const int width,
|
||||
const int amount)
|
||||
GraphicsDevice *g, const Pic *icon, const struct vec2i pos,
|
||||
const int width, const int amount)
|
||||
{
|
||||
const int dx = width / MAX_GRENADE_ICONS;
|
||||
for (int i = 0; i < amount; i++)
|
||||
@@ -476,8 +479,8 @@ static void DrawGrenadeIcons(
|
||||
}
|
||||
|
||||
static void DrawRadar(
|
||||
GraphicsDevice *device, const TActor *p,
|
||||
const int flags, const bool showExit)
|
||||
GraphicsDevice *device, const TActor *p, const int flags,
|
||||
const bool showExit)
|
||||
{
|
||||
struct vec2i pos = svec2i_zero();
|
||||
int w = device->cachedConfig.Res.x;
|
||||
@@ -568,19 +571,15 @@ static void DrawRadar(
|
||||
{
|
||||
const struct vec2i playerPos = Vec2ToTile(p->thing.Pos);
|
||||
AutomapDrawRegion(
|
||||
device->gameWindow.renderer,
|
||||
&gMap,
|
||||
pos,
|
||||
svec2i(AUTOMAP_SIZE, AUTOMAP_SIZE),
|
||||
playerPos,
|
||||
AUTOMAP_FLAGS_MASK,
|
||||
device->gameWindow.renderer, &gMap, pos,
|
||||
svec2i(AUTOMAP_SIZE, AUTOMAP_SIZE), playerPos, AUTOMAP_FLAGS_MASK,
|
||||
showExit);
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawHealth(
|
||||
GraphicsDevice *g, const TActor *a,
|
||||
const int flags, const HUDPlayer *h, const Rect2i r, const color_t mask)
|
||||
GraphicsDevice *g, const TActor *a, const int flags, const HUDPlayer *h,
|
||||
const Rect2i r, const color_t mask)
|
||||
{
|
||||
const int right =
|
||||
SCORE_WIDTH + GRENADES_WIDTH + AMMO_WIDTH + GUN_ICON_WIDTH;
|
||||
@@ -643,7 +642,7 @@ static void DrawPlayerObjectiveCompass(
|
||||
}
|
||||
|
||||
static void DrawCompassArrow(
|
||||
GraphicsDevice *g,const Rect2i r, const struct vec2 pos,
|
||||
GraphicsDevice *g, const Rect2i r, const struct vec2 pos,
|
||||
const struct vec2 playerPos, const color_t mask, const char *label);
|
||||
static void DrawObjectiveCompass(
|
||||
GraphicsDevice *g, const struct vec2 playerPos, const Rect2i r,
|
||||
@@ -665,23 +664,23 @@ static void DrawObjectiveCompass(
|
||||
{
|
||||
Tile *tile = MapGetTile(map, tilePos);
|
||||
CA_FOREACH(ThingId, tid, tile->things)
|
||||
Thing *ti = ThingIdGetThing(tid);
|
||||
if (!(ti->flags & THING_OBJECTIVE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const int objective = ObjectiveFromThing(ti->flags);
|
||||
const Objective *o =
|
||||
CArrayGet(&gMission.missionData->Objectives, objective);
|
||||
if (o->Flags & OBJECTIVE_HIDDEN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!(o->Flags & OBJECTIVE_POSKNOWN) && !tile->isVisited)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
DrawCompassArrow(g, r, ti->Pos, playerPos, o->color, NULL);
|
||||
Thing *ti = ThingIdGetThing(tid);
|
||||
if (!(ti->flags & THING_OBJECTIVE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const int objective = ObjectiveFromThing(ti->flags);
|
||||
const Objective *o =
|
||||
CArrayGet(&gMission.missionData->Objectives, objective);
|
||||
if (o->Flags & OBJECTIVE_HIDDEN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!(o->Flags & OBJECTIVE_POSKNOWN) && !tile->isVisited)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
DrawCompassArrow(g, r, ti->Pos, playerPos, o->color, NULL);
|
||||
CA_FOREACH_END()
|
||||
}
|
||||
}
|
||||
@@ -689,7 +688,7 @@ static void DrawObjectiveCompass(
|
||||
|
||||
#define COMP_SATURATE_DIST 350
|
||||
static void DrawCompassArrow(
|
||||
GraphicsDevice *g,const Rect2i r, const struct vec2 pos,
|
||||
GraphicsDevice *g, const Rect2i r, const struct vec2 pos,
|
||||
const struct vec2 playerPos, const color_t mask, const char *label)
|
||||
{
|
||||
const struct vec2 compassV = svec2_subtract(
|
||||
@@ -705,9 +704,10 @@ static void DrawCompassArrow(
|
||||
int xDist = (int)fabsf(compassV.x) - r.Size.x / 2;
|
||||
int yDist = (int)fabsf(compassV.y) - r.Size.y / 2;
|
||||
int lDist;
|
||||
xDist > yDist ? lDist = xDist: (lDist = yDist);
|
||||
HSV hsv = { -1.0, 1.0,
|
||||
2.0 - 1.5 * MIN(lDist, COMP_SATURATE_DIST) / COMP_SATURATE_DIST };
|
||||
xDist > yDist ? lDist = xDist : (lDist = yDist);
|
||||
HSV hsv = {
|
||||
-1.0, 1.0,
|
||||
2.0 - 1.5 * MIN(lDist, COMP_SATURATE_DIST) / COMP_SATURATE_DIST};
|
||||
const color_t tintedMask = ColorTint(mask, hsv);
|
||||
struct vec2i textPos = svec2i_zero();
|
||||
struct vec2i drawPos = svec2i_zero();
|
||||
@@ -725,10 +725,10 @@ static void DrawCompassArrow(
|
||||
if (compassV.x > 0)
|
||||
{
|
||||
// right edge
|
||||
textPos = svec2i(
|
||||
r.Pos.x + r.Size.x, r.Pos.y + r.Size.y / 2 + yInt);
|
||||
drawPos = svec2i(
|
||||
textPos.x - p->size.x, textPos.y - p->size.y / 2);
|
||||
textPos =
|
||||
svec2i(r.Pos.x + r.Size.x, r.Pos.y + r.Size.y / 2 + yInt);
|
||||
drawPos =
|
||||
svec2i(textPos.x - p->size.x, textPos.y - p->size.y / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -749,10 +749,10 @@ static void DrawCompassArrow(
|
||||
if (compassV.y > 0)
|
||||
{
|
||||
// bottom edge
|
||||
textPos = svec2i(
|
||||
r.Pos.x + r.Size.x / 2 + xInt, r.Pos.y + r.Size.y);
|
||||
drawPos = svec2i(
|
||||
textPos.x - p->size.x / 2, textPos.y - p->size.y);
|
||||
textPos =
|
||||
svec2i(r.Pos.x + r.Size.x / 2 + xInt, r.Pos.y + r.Size.y);
|
||||
drawPos =
|
||||
svec2i(textPos.x - p->size.x / 2, textPos.y - p->size.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -777,9 +777,11 @@ static void DrawCompassArrow(
|
||||
// Make sure the text is inside the screen
|
||||
int padding = 8;
|
||||
textPos.x = MAX(textPos.x, r.Pos.x + padding);
|
||||
textPos.x = MIN(textPos.x, r.Pos.x + r.Size.x - textSize.x - padding);
|
||||
textPos.x =
|
||||
MIN(textPos.x, r.Pos.x + r.Size.x - textSize.x - padding);
|
||||
textPos.y = MAX(textPos.y, r.Pos.y + padding);
|
||||
textPos.y = MIN(textPos.y, r.Pos.y + r.Size.y - textSize.y - padding);
|
||||
textPos.y =
|
||||
MIN(textPos.y, r.Pos.y + r.Size.y - textSize.y - padding);
|
||||
FontStrMask(label, textPos, tintedMask);
|
||||
}
|
||||
}
|
||||
|
@@ -281,6 +281,7 @@ int MapArchiveSave(const char *filename, CampaignSetting *c)
|
||||
AddStringPair(root, "Title", c->Title);
|
||||
AddStringPair(root, "Author", c->Author);
|
||||
AddStringPair(root, "Description", c->Description);
|
||||
AddBoolPair(root, "Ammo", c->Ammo);
|
||||
AddBoolPair(root, "WeaponPersist", c->WeaponPersist);
|
||||
AddBoolPair(root, "SkipWeaponMenu", c->SkipWeaponMenu);
|
||||
AddIntPair(root, "Missions", (int)c->Missions.size);
|
||||
|
@@ -256,7 +256,7 @@ bool MapTryPlaceOneObject(
|
||||
const int extraFlags, const bool isStrictMode)
|
||||
{
|
||||
// Don't place ammo spawners if ammo is disabled
|
||||
if (!ConfigGetBool(&gConfig, "Game.Ammo") &&
|
||||
if (!gCampaign.Setting.Ammo &&
|
||||
mo->Type == MAP_OBJECT_TYPE_PICKUP_SPAWNER &&
|
||||
mo->u.PickupClass->Type == PICKUP_AMMO)
|
||||
{
|
||||
|
@@ -176,6 +176,7 @@ void MapNewLoadCampaignJSON(json_t *root, CampaignSetting *c)
|
||||
c->Author = GetString(root, "Author");
|
||||
CFREE(c->Description);
|
||||
c->Description = GetString(root, "Description");
|
||||
LoadBool(&c->Ammo, root, "Ammo");
|
||||
LoadBool(&c->WeaponPersist, root, "WeaponPersist");
|
||||
LoadBool(&c->SkipWeaponMenu, root, "SkipWeaponMenu");
|
||||
}
|
||||
|
@@ -420,7 +420,6 @@ void NetServerSendGameStartMessages(NetServer *n, const int peerId)
|
||||
// Send all game-specific config values
|
||||
SendConfig(&gConfig, "Game.FriendlyFire", n, peerId);
|
||||
SendConfig(&gConfig, "Game.FPS", n, peerId);
|
||||
SendConfig(&gConfig, "Game.Ammo", n, peerId);
|
||||
SendConfig(&gConfig, "Game.Fog", n, peerId);
|
||||
SendConfig(&gConfig, "Game.SightRange", n, peerId);
|
||||
SendConfig(&gConfig, "Game.AllyCollision", n, peerId);
|
||||
|
@@ -151,7 +151,7 @@ static void AddPickupAtObject(const TObject *o, const PickupType type)
|
||||
strcpy(e.u.AddPickup.PickupClass, "health");
|
||||
break;
|
||||
case PICKUP_AMMO:
|
||||
if (!ConfigGetBool(&gConfig, "Game.Ammo"))
|
||||
if (!gCampaign.Setting.Ammo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -257,7 +257,7 @@ static bool HasGunUsingAmmo(const TActor *a, const int ammoId)
|
||||
static bool TryPickupAmmo(TActor *a, const Pickup *p, const char **sound)
|
||||
{
|
||||
// Don't pickup if not using ammo
|
||||
if (!ConfigGetBool(&gConfig, "Game.Ammo"))
|
||||
if (!gCampaign.Setting.Ammo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@@ -178,7 +178,7 @@ void AmmoSpawnerInit(PowerupSpawner *p, Map *map, const int ammoId)
|
||||
{
|
||||
PowerupSpawnerInit(p, map);
|
||||
// TODO: disable ammo spawners unless classic mode
|
||||
p->Enabled = ConfigGetBool(&gConfig, "Game.Ammo") && !gCampaign.IsClient;
|
||||
p->Enabled = gCampaign.Setting.Ammo && !gCampaign.IsClient;
|
||||
p->SpawnTime = AMMO_SPAWN_TIME;
|
||||
p->RateScaleFunc = AmmoScale;
|
||||
p->PlaceFunc = AmmoPlace;
|
||||
|
@@ -1247,8 +1247,6 @@ int main(int argc, char *argv[])
|
||||
ConfigGet(&gConfig, "Graphics.WindowWidth")->u.Int.Value = 800;
|
||||
ConfigGet(&gConfig, "Graphics.WindowHeight")->u.Int.Value = 600;
|
||||
ConfigGet(&gConfig, "Graphics.SecondWindow")->u.Bool.Value = false;
|
||||
// Force enable ammo so that ammo spawners show up
|
||||
ConfigGet(&gConfig, "Game.Ammo")->u.Bool.Value = true;
|
||||
ConfigSetChanged(&gConfig);
|
||||
GraphicsInit(ec.g, &gConfig);
|
||||
ec.g->cachedConfig.IsEditor = true;
|
||||
|
@@ -102,6 +102,14 @@ static bool Draw(SDL_Window *win, struct nk_context *ctx, void *data)
|
||||
// TODO: text area? Nuklear doesn't support it yet
|
||||
DrawTextbox(ctx, cData->Description, 1024, "Description", NK_EDIT_BOX);
|
||||
nk_layout_row_dynamic(ctx, ROW_HEIGHT, 1);
|
||||
if (DrawCheckbox(
|
||||
ctx, "Ammo",
|
||||
"Enable ammo; if disabled all weapons have infinite ammo and "
|
||||
"use up score instead",
|
||||
&cData->c->Setting.Ammo))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
if (DrawCheckbox(
|
||||
ctx, "Persist weapons & ammo",
|
||||
"Whether weapons picked up and extra ammo are "
|
||||
|
@@ -557,7 +557,6 @@ GameLoopData *GameOptions(const GameMode gm)
|
||||
I("Game.PlayerHP");
|
||||
I("Game.Lives");
|
||||
I("Game.HealthPickups");
|
||||
I("Game.Ammo");
|
||||
I("Game.RandomSeed");
|
||||
MenuAddSubmenu(ms->root, MenuCreateSeparator(""));
|
||||
I("StartServer");
|
||||
@@ -566,7 +565,6 @@ GameLoopData *GameOptions(const GameMode gm)
|
||||
I("Dogfight.PlayerHP");
|
||||
I("Dogfight.FirstTo");
|
||||
I("Game.HealthPickups");
|
||||
I("Game.Ammo");
|
||||
I("Game.RandomSeed");
|
||||
MenuAddSubmenu(ms->root,
|
||||
MenuCreateAllowedWeapons("Weapons...", data));
|
||||
@@ -577,7 +575,6 @@ GameLoopData *GameOptions(const GameMode gm)
|
||||
I("Game.PlayerHP");
|
||||
I("Deathmatch.Lives");
|
||||
I("Game.HealthPickups");
|
||||
I("Game.Ammo");
|
||||
I("Game.RandomSeed");
|
||||
MenuAddSubmenu(ms->root,
|
||||
MenuCreateAllowedWeapons("Weapons...", data));
|
||||
|
Reference in New Issue
Block a user