-
Notifications
You must be signed in to change notification settings - Fork 6
/
AutoMower.cs
114 lines (94 loc) · 3.98 KB
/
AutoMower.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
using System.Collections.Generic;
using UnityEngine;
namespace GrassyKnight
{
class AutoMower : MonoBehaviour {
// The last time we searched for grass
private float _lastSearchedAt = float.NegativeInfinity;
// The number of seconds to wait between searches
public float SearchInterval = 0.5f;
// The grass knower that will tell us what to try and cut. Must be set
// to a non-null value or the AutoMower won't do anything.
public GrassKnower SetOfAllGrass = null;
// The GrassDB that will tell us what's been cut already. Must be set
// to a non-null value or the AutoMower won't do anything.
public GrassDB GrassStates = null;
public void Update() {
try {
_Update();
} catch (System.Exception e) {
GrassyKnight.Instance.LogException(
"Error in AutoMower.Update()", e);
}
}
private void _Update() {
if (SetOfAllGrass == null || GrassStates == null) {
return;
}
if (_lastSearchedAt + SearchInterval <= Time.time) {
foreach (GameObject grass in GetGrassOnScreen()) {
if (!SlashGrass(grass)) {
GrassyKnight.Instance.LogError(
$"Failed to slash on screen grass.");
break;
}
}
_lastSearchedAt = Time.time;
}
}
private static bool IsPointOnScreenWithPadding(Vector2 point, float padding = 0.1f) {
Vector2 viewLocalPoint =
UnityEngine.Camera.main.WorldToViewportPoint(point);
return
viewLocalPoint.x >= padding &&
viewLocalPoint.x <= 1 - padding &&
viewLocalPoint.y >= padding &&
viewLocalPoint.y <= 1 - padding;
}
private List<GameObject> GetGrassOnScreen() {
List<GameObject> result = new List<GameObject>();
foreach (GameObject maybeGrass in
UnityEngine.Object.FindObjectsOfType<GameObject>())
{
GrassKey k = GrassKey.FromGameObject(maybeGrass);
GrassState? state = GrassStates.TryGet(k);
bool isUncut = state == GrassState.Uncut || state == null;
if (isUncut &&
IsPointOnScreenWithPadding(maybeGrass.transform.position) &&
SetOfAllGrass.IsGrass(maybeGrass)) {
result.Add(maybeGrass);
}
}
return result;
}
private bool SlashGrass(GameObject grass) {
HeroController heroController = GameManager.instance?.hero_ctrl;
if (heroController == null) {
return false;
}
GameObject slashPrefab = heroController.slashPrefab;
if (slashPrefab == null) {
return false;
}
// We expect to be attached to the hero, so we could also just use
// our gameObject here, but eh. Feels a bit better stylistically to
// not assume what we're attached to 🤷♀️.
Transform attacksParent =
heroController.gameObject?.transform.Find("Attacks");
if (attacksParent == null) {
return false;
}
GameObject slash = Instantiate(slashPrefab, attacksParent);
slash.transform.position = grass.transform.position;
slash.GetComponent<NailSlash>().StartSlash();
// I'm not positive, but I think the slashes just leak if we don't
// do this...
tk2dSpriteAnimator animator =
slash.GetComponent<tk2dSpriteAnimator>();
float lifetime =
animator.DefaultClip.frames.Length / animator.ClipFps;
Destroy(slash, lifetime);
return true;
}
}
}