Skip to content

Commit 5aa2d51

Browse files
authored
Merge pull request KaBooMa#27 from HazDS/haz-cartel
Extends the Cartel API with new functionality for spawning/controlling goons and managing regional influence.
2 parents 45b890c + cb2575a commit 5aa2d51

File tree

7 files changed

+1007
-2
lines changed

7 files changed

+1007
-2
lines changed

S1API/Cartel/Cartel.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,47 @@ public static Cartel? Instance
8080
public int HoursSinceStatusChange =>
8181
S1Cartel.HoursSinceStatusChange;
8282

83+
/// <summary>
84+
/// Gets the goon manager for spawning and controlling cartel goons.
85+
/// Returns null if the goon pool is not available.
86+
/// </summary>
87+
public GoonManager? GoonPool
88+
{
89+
get
90+
{
91+
var pool = S1Cartel.GoonPool;
92+
if (pool == null)
93+
return null;
94+
return new GoonManager(pool);
95+
}
96+
}
97+
98+
/// <summary>
99+
/// Gets the cartel influence manager for regional influence tracking.
100+
/// Returns null if the influence system is not available.
101+
/// </summary>
102+
public CartelInfluence? Influence
103+
{
104+
get
105+
{
106+
var influence = S1Cartel.Influence;
107+
if (influence == null)
108+
return null;
109+
return new CartelInfluence(influence);
110+
}
111+
}
112+
113+
/// <summary>
114+
/// Sets the cartel status. This is a server RPC that will sync to all clients.
115+
/// </summary>
116+
/// <param name="status">The new cartel status.</param>
117+
/// <param name="resetTimer">Whether to reset the hours since status change timer.</param>
118+
public void SetStatus(CartelStatus status, bool resetTimer = true)
119+
{
120+
// Cast through int since our CartelStatus values match the game's ECartelStatus values
121+
S1Cartel.SetStatus_Server((ECartelStatus)(int)status, resetTimer);
122+
}
123+
83124
/// <summary>
84125
/// Event fired when the Cartel status changes.
85126
/// Provides the old status and new status as parameters.

S1API/Cartel/CartelGoon.cs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#if (IL2CPPMELON)
2+
using S1Cartel = Il2CppScheduleOne.Cartel;
3+
using S1Player = Il2CppScheduleOne.PlayerScripts;
4+
using S1Combat = Il2CppScheduleOne.Combat;
5+
#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
6+
using S1Cartel = ScheduleOne.Cartel;
7+
using S1Player = ScheduleOne.PlayerScripts;
8+
using S1Combat = ScheduleOne.Combat;
9+
#endif
10+
11+
using S1API.Entities;
12+
using S1API.Entities.Interfaces;
13+
using UnityEngine;
14+
15+
namespace S1API.Cartel
16+
{
17+
/// <summary>
18+
/// Represents a cartel goon (enemy NPC) that can be spawned and controlled.
19+
/// </summary>
20+
public class CartelGoon
21+
{
22+
/// <summary>
23+
/// INTERNAL: Reference to the game's CartelGoon instance.
24+
/// </summary>
25+
internal readonly S1Cartel.CartelGoon S1Goon;
26+
27+
/// <summary>
28+
/// INTERNAL: Constructor to create a wrapper from a game CartelGoon instance.
29+
/// </summary>
30+
internal CartelGoon(S1Cartel.CartelGoon goon)
31+
{
32+
S1Goon = goon;
33+
}
34+
35+
/// <summary>
36+
/// Whether this goon is still conscious (alive and not knocked out).
37+
/// </summary>
38+
public bool IsConscious => S1Goon.IsConscious;
39+
40+
/// <summary>
41+
/// Whether this goon is currently spawned in the world.
42+
/// </summary>
43+
public bool IsSpawned => S1Goon.IsSpawned;
44+
45+
/// <summary>
46+
/// The current world position of this goon.
47+
/// </summary>
48+
public Vector3 Position => S1Goon.transform.position;
49+
50+
/// <summary>
51+
/// The GameObject associated with this goon.
52+
/// </summary>
53+
public GameObject GameObject => S1Goon.gameObject;
54+
55+
/// <summary>
56+
/// Teleports this goon to a specific world position.
57+
/// </summary>
58+
/// <param name="position">The target position.</param>
59+
public void WarpTo(Vector3 position)
60+
{
61+
S1Goon.Movement?.Warp(position);
62+
}
63+
64+
/// <summary>
65+
/// Makes this goon attack the local player.
66+
/// </summary>
67+
public void AttackPlayer()
68+
{
69+
var player = S1Player.Player.Local;
70+
if (player == null) return;
71+
72+
var targetable = player.GetComponent<S1Combat.ICombatTargetable>();
73+
if (targetable != null)
74+
{
75+
S1Goon.AttackEntity(targetable);
76+
}
77+
}
78+
79+
/// <summary>
80+
/// Makes this goon attack a specific entity.
81+
/// </summary>
82+
/// <param name="target">The target entity to attack.</param>
83+
public void Attack(IEntity target)
84+
{
85+
if (target?.gameObject == null) return;
86+
87+
// Get the ICombatTargetable component for attacking
88+
var targetable = target.gameObject.GetComponent<S1Combat.ICombatTargetable>();
89+
if (targetable != null)
90+
{
91+
S1Goon.AttackEntity(targetable);
92+
}
93+
}
94+
95+
/// <summary>
96+
/// Despawns this goon, removing them from the world.
97+
/// </summary>
98+
public void Despawn()
99+
{
100+
S1Goon.Despawn();
101+
}
102+
103+
/// <summary>
104+
/// Sets or clears the default weapon for this goon.
105+
/// Pass null to make them use fists.
106+
/// </summary>
107+
/// <param name="weaponAssetPath">The weapon asset path, or null for fists.</param>
108+
public void SetDefaultWeapon(string? weaponAssetPath)
109+
{
110+
var combatBehaviour = S1Goon.Behaviour?.CombatBehaviour;
111+
if (combatBehaviour == null) return;
112+
113+
if (string.IsNullOrEmpty(weaponAssetPath))
114+
{
115+
combatBehaviour.DefaultWeapon = null;
116+
}
117+
else
118+
{
119+
var go = Resources.Load(weaponAssetPath) as GameObject;
120+
if (go != null)
121+
{
122+
#if (IL2CPPMELON)
123+
var equippable = UnityEngine.Object.Instantiate(go).GetComponent<Il2CppScheduleOne.AvatarFramework.Equipping.AvatarWeapon>();
124+
#else
125+
var equippable = UnityEngine.Object.Instantiate(go).GetComponent<ScheduleOne.AvatarFramework.Equipping.AvatarWeapon>();
126+
#endif
127+
combatBehaviour.DefaultWeapon = equippable;
128+
}
129+
}
130+
}
131+
}
132+
}

S1API/Cartel/CartelInfluence.cs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#if (IL2CPPMELON)
2+
using S1Cartel = Il2CppScheduleOne.Cartel;
3+
using EMapRegion = Il2CppScheduleOne.Map.EMapRegion;
4+
#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
5+
using S1Cartel = ScheduleOne.Cartel;
6+
using EMapRegion = ScheduleOne.Map.EMapRegion;
7+
#endif
8+
9+
using System;
10+
using S1API.Map;
11+
12+
namespace S1API.Cartel
13+
{
14+
/// <summary>
15+
/// Provides access to cartel influence per map region.
16+
/// Influence ranges from 0 (no cartel presence) to 1 (full cartel control).
17+
/// </summary>
18+
public class CartelInfluence
19+
{
20+
/// <summary>
21+
/// INTERNAL: Reference to the game's CartelInfluence instance.
22+
/// </summary>
23+
internal readonly S1Cartel.CartelInfluence S1Influence;
24+
25+
/// <summary>
26+
/// INTERNAL: Constructor to create a wrapper from a game CartelInfluence instance.
27+
/// </summary>
28+
internal CartelInfluence(S1Cartel.CartelInfluence influence)
29+
{
30+
S1Influence = influence;
31+
}
32+
33+
/// <summary>
34+
/// Gets the cartel influence level for a specific region.
35+
/// </summary>
36+
/// <param name="region">The map region to check.</param>
37+
/// <returns>Influence level from 0.0 to 1.0</returns>
38+
public float GetInfluence(Region region)
39+
{
40+
return S1Influence.GetInfluence(ConvertToGameRegion(region));
41+
}
42+
43+
/// <summary>
44+
/// Changes the cartel influence in a region by a specified amount.
45+
/// Positive values increase influence, negative values decrease it.
46+
/// </summary>
47+
/// <param name="region">The map region to modify.</param>
48+
/// <param name="amount">The amount to change (-1.0 to 1.0).</param>
49+
public void ChangeInfluence(Region region, float amount)
50+
{
51+
S1Influence.ChangeInfluence(ConvertToGameRegion(region), amount);
52+
}
53+
54+
#if !IL2CPPMELON
55+
/// <summary>
56+
/// Event fired when cartel influence changes in any region.
57+
/// Parameters: region, old influence, new influence.
58+
/// Note: This event is only available in Mono builds.
59+
/// </summary>
60+
public event Action<Region, float, float> OnInfluenceChanged
61+
{
62+
add
63+
{
64+
if (value == null) return;
65+
S1Influence.OnInfluenceChanged += (gameRegion, oldVal, newVal) =>
66+
{
67+
value?.Invoke(ConvertFromGameRegion(gameRegion), oldVal, newVal);
68+
};
69+
}
70+
remove
71+
{
72+
// Note: Event removal is not fully supported due to delegate wrapping
73+
}
74+
}
75+
76+
/// <summary>
77+
/// Converts game's EMapRegion to S1API Region.
78+
/// </summary>
79+
private static Region ConvertFromGameRegion(EMapRegion region)
80+
{
81+
return (Region)(int)region;
82+
}
83+
#endif
84+
85+
/// <summary>
86+
/// Converts S1API Region to game's EMapRegion.
87+
/// </summary>
88+
private static EMapRegion ConvertToGameRegion(Region region)
89+
{
90+
return (EMapRegion)(int)region;
91+
}
92+
}
93+
}

S1API/Cartel/GoonManager.cs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#if (IL2CPPMELON)
2+
using S1Cartel = Il2CppScheduleOne.Cartel;
3+
#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
4+
using S1Cartel = ScheduleOne.Cartel;
5+
#endif
6+
7+
using System.Collections.Generic;
8+
using UnityEngine;
9+
10+
namespace S1API.Cartel
11+
{
12+
/// <summary>
13+
/// Manages the spawning and tracking of cartel goons.
14+
/// Provides access to the game's goon pool for spawning hostile NPCs.
15+
/// </summary>
16+
public class GoonManager
17+
{
18+
/// <summary>
19+
/// INTERNAL: Reference to the game's GoonPool instance.
20+
/// </summary>
21+
internal readonly S1Cartel.GoonPool S1GoonPool;
22+
23+
/// <summary>
24+
/// INTERNAL: Constructor to create a wrapper from a game GoonPool instance.
25+
/// </summary>
26+
internal GoonManager(S1Cartel.GoonPool goonPool)
27+
{
28+
S1GoonPool = goonPool;
29+
}
30+
31+
/// <summary>
32+
/// The number of goons available to spawn from the pool.
33+
/// </summary>
34+
public int AvailableGoonCount => S1GoonPool.UnspawnedGoonCount;
35+
36+
/// <summary>
37+
/// Spawns a single goon at the specified position.
38+
/// </summary>
39+
/// <param name="position">The world position to spawn the goon at.</param>
40+
/// <returns>The spawned goon, or null if no goons are available.</returns>
41+
public CartelGoon? SpawnGoon(Vector3 position)
42+
{
43+
if (AvailableGoonCount == 0)
44+
return null;
45+
46+
var goons = S1GoonPool.SpawnMultipleGoons(position, 1);
47+
if (goons == null || goons.Count == 0)
48+
return null;
49+
50+
return new CartelGoon(goons[0]);
51+
}
52+
53+
/// <summary>
54+
/// Spawns multiple goons at the specified position.
55+
/// </summary>
56+
/// <param name="position">The world position to spawn the goons at.</param>
57+
/// <param name="count">The number of goons to spawn.</param>
58+
/// <returns>A list of spawned goons. May contain fewer than requested if pool is depleted.</returns>
59+
public List<CartelGoon> SpawnGoons(Vector3 position, int count)
60+
{
61+
var result = new List<CartelGoon>();
62+
63+
if (count <= 0 || AvailableGoonCount == 0)
64+
return result;
65+
66+
int toSpawn = System.Math.Min(count, AvailableGoonCount);
67+
var goons = S1GoonPool.SpawnMultipleGoons(position, toSpawn);
68+
69+
if (goons == null)
70+
return result;
71+
72+
#if (IL2CPPMELON)
73+
foreach (var goon in goons)
74+
{
75+
if (goon != null)
76+
result.Add(new CartelGoon(goon));
77+
}
78+
#else
79+
foreach (var goon in goons)
80+
{
81+
if (goon != null)
82+
result.Add(new CartelGoon(goon));
83+
}
84+
#endif
85+
86+
return result;
87+
}
88+
89+
/// <summary>
90+
/// Spawns goons at multiple positions, one goon per position.
91+
/// </summary>
92+
/// <param name="positions">The positions to spawn goons at.</param>
93+
/// <returns>A list of spawned goons with their positions set.</returns>
94+
public List<CartelGoon> SpawnGoonsAtPositions(Vector3[] positions)
95+
{
96+
var result = new List<CartelGoon>();
97+
98+
if (positions == null || positions.Length == 0)
99+
return result;
100+
101+
// Spawn all at first position, then warp to individual positions
102+
var goons = SpawnGoons(positions[0], positions.Length);
103+
104+
for (int i = 0; i < goons.Count && i < positions.Length; i++)
105+
{
106+
goons[i].WarpTo(positions[i]);
107+
result.Add(goons[i]);
108+
}
109+
110+
return result;
111+
}
112+
}
113+
}

0 commit comments

Comments
 (0)