summaryrefslogtreecommitdiff
path: root/Oneko.cs
blob: a3d0a1c80fd04d9c943e7b1ca6f787ecb8a5ecd6 (plain)
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
using System.Numerics;
using OnekoOnline.Net;
using Raylib_cs;

namespace OnekoOnline;

class Oneko : Drawable
{
    public readonly Bitmap SpriteSheet;
    Vector2 TargetPosition;
    OnekoAnimation Animation = ScratchSelf;

    float updateTimer = 0f;
    const float updateRate = 1f/5f;
    int Frame = 0;

    public static Oneko? LocalOneko;

    public static Dictionary<int, Oneko> NetNekos = [];

    public Oneko()
    {
        Size = new(32,32);
        Position = new(320/2, 240/2);

        string SpriteSheetPath = OnekoOnline.Config.GetValue("SpriteSheetPath", "oneko.png");

        if (File.Exists(SpriteSheetPath) && new FileInfo(SpriteSheetPath).Length < 128*256*3) {
            SpriteSheet = Bitmap.FromPNGMemory(File.ReadAllBytes(SpriteSheetPath));
        } else {
            Console.WriteLine("Path to spritesheet was invalid, using the default.");
            SpriteSheet = Bitmap.FromPNGMemory(EmbeddedResources.GetResource("oneko.png"));
        }

        LocalOneko ??= this;

        Drawables.Add(this);
    }

    public Oneko(Bitmap spriteSheet)
    {
        Size = new(32,32);
        Position = new(0, 0);
        SpriteSheet = spriteSheet;

        Drawables.Add(this);
    }

    public override void Draw()
    {
        Raylib.DrawTexturePro(SpriteSheet.Texture, Animation.GetFrame(Frame), new Rectangle(Position.X, Position.Y, Size.X, Size.Y), Size/2, Rotation, Color.WHITE);
    }

    public override void Update(float delta)
    {
        if (this == LocalOneko) {
            foreach (User user in OnekoOnline.Client!.Users.Values) {
                if (user.ExchangedData && !NetNekos.ContainsKey(user.Id)) {
                    Bitmap spriteSheet = Bitmap.Deserialize(user.SpriteSheet);
                    NetNekos.Add(user.Id, new Oneko(spriteSheet));
                }
            }
        }

        updateTimer += delta;
        if (updateTimer < updateRate) return;

        if (Raylib.IsWindowFocused()) {
            TargetPosition = Raylib.GetMousePosition()/2;
        } else {
            TargetPosition = new Vector2(320/2, 240/2);
        }
        Vector2 TargetDirection = (TargetPosition-Position).LimitLength(10f);
        if (TargetDirection.Length() > 1) Animation = GetDirectionalRun(TargetDirection);
        else Animation = Idle;
        Position += TargetDirection;

        Frame = (Frame + 1) % 2;

        updateTimer = 0f;
    }

    public override void Dispose()
    {
        SpriteSheet.Dispose();
        Drawables.Remove(this);
    }

    struct OnekoAnimation(Rectangle frame1, Rectangle frame2)
    {
        public Rectangle Frame1 = frame1;
        Rectangle Frame2 = frame2;

        public readonly Rectangle GetFrame(int frame) {
            return (frame % 2 == 0) ? Frame1 : Frame2;
        }
    }

    static OnekoAnimation GetDirectionalRun(Vector2 direction)
    {
        direction = Vector2.Normalize(direction);
        
        Vector2 nearestDir = Directions.Up;
        float nearestDistance = 10f;
        foreach (Vector2 dir in Directions.AllDirections) {
            float distanceCheck = Vector2.Distance(direction, dir);
            if (distanceCheck < nearestDistance) {
                nearestDir = dir;
                nearestDistance = distanceCheck;
            }
        }

        return RunDirections[nearestDir];
    }

    static readonly Rectangle Idle1 = new(32*3, 32*3, 32, 32);
    static readonly Rectangle Alert1 = new(32*7, 32*3, 32, 32);
    static readonly Rectangle Yawn1 = new(32*3, 32*2, 32, 32);
    static readonly Rectangle Clean1 = new(32*7, 0, 32, 32);
    static readonly Rectangle Scratch1 = new(32*5, 0, 32, 32);
    static readonly Rectangle Scratch2 = new(32*6, 0, 32, 32);
    static readonly Rectangle Sleep1 = new(32*2, 0, 32, 32);
    static readonly Rectangle Sleep2 = new(32*2, 32, 32, 32);
    static readonly Rectangle RunUp1 = new(32, 32*3, 32, 32);
    static readonly Rectangle RunUp2 = new(32, 32*2, 32, 32);
    static readonly Rectangle RunUpLeft1 = new(32, 32, 32, 32);
    static readonly Rectangle RunUpLeft2 = new(32, 0, 32, 32);
    static readonly Rectangle RunLeft1 = new(32*4, 32*3, 32, 32);
    static readonly Rectangle RunLeft2 = new(32*4, 32*2, 32, 32);
    static readonly Rectangle RunDownLeft1 = new(32*6, 32, 32, 32);
    static readonly Rectangle RunDownLeft2 = new(32*5, 32*3, 32, 32);
    static readonly Rectangle RunDown1 = new(32*7, 32*2, 32, 32);
    static readonly Rectangle RunDown2 = new(32*6, 32*3, 32, 32);
    static readonly Rectangle RunDownRight1 = new(32*5, 32*2, 32, 32);
    static readonly Rectangle RunDownRight2 = new(32*5, 32, 32, 32);
    static readonly Rectangle RunRight1 = new(32*3, 32, 32, 32);
    static readonly Rectangle RunRight2 = new(32*3, 0, 32, 32);
    static readonly Rectangle RunUpRight1 = new(0, 32*3, 32, 32);
    static readonly Rectangle RunUpRight2 = new(0, 32*2, 32, 32);
    static readonly Rectangle ScratchUp1 = new(0, 0, 32, 32);
    static readonly Rectangle ScratchUp2 = new(0, 32, 32, 32);
    static readonly Rectangle ScratchLeft1 = new(32*4, 0, 32, 32);
    static readonly Rectangle ScratchLeft2 = new(32*4, 32, 32, 32);
    static readonly Rectangle ScratchDown1 = new(32*7, 32, 32, 32);
    static readonly Rectangle ScratchDown2 = new(32*6, 32*2, 32, 32);
    static readonly Rectangle ScratchRight1 = new(32*2, 32*2, 32, 32);
    static readonly Rectangle ScratchRight2 = new(32*2, 32*3, 32, 32);

    static readonly OnekoAnimation Idle = new(Idle1, Idle1);
    static readonly OnekoAnimation Alert = new(Alert1, Alert1);
    static readonly OnekoAnimation Yawn = new(Yawn1, Yawn1);
    static readonly OnekoAnimation Clean = new(Clean1, Clean1);
    static readonly OnekoAnimation ScratchSelf = new(Scratch1, Scratch2);
    static readonly OnekoAnimation Sleep = new(Sleep1, Sleep2);
    static readonly OnekoAnimation RunUp = new(RunUp1, RunUp2);
    static readonly OnekoAnimation RunUpLeft = new(RunUpLeft1, RunUpLeft2);
    static readonly OnekoAnimation RunLeft = new(RunLeft1, RunLeft2);
    static readonly OnekoAnimation RunDownLeft = new(RunDownLeft1, RunDownLeft2);
    static readonly OnekoAnimation RunDown = new(RunDown1, RunDown2);
    static readonly OnekoAnimation RunDownRight = new(RunDownRight1, RunDownRight2);
    static readonly OnekoAnimation RunRight = new(RunRight1, RunRight2);
    static readonly OnekoAnimation RunUpRight = new(RunUpRight1, RunUpRight2);
    static readonly OnekoAnimation ScratchUp = new(ScratchUp1, ScratchUp2);
    static readonly OnekoAnimation ScratchLeft = new(ScratchLeft1, ScratchLeft2);
    static readonly OnekoAnimation ScratchDown = new(ScratchDown1, ScratchDown2);
    static readonly OnekoAnimation ScratchRight = new(ScratchRight1, ScratchRight2);

    static readonly Dictionary<Vector2, OnekoAnimation> RunDirections = new() {
        {Directions.Up, RunUp},
        {Directions.Down, RunDown},
        {Directions.Left, RunLeft},
        {Directions.Right, RunRight},
        {Directions.UpLeft, RunUpLeft},
        {Directions.UpRight, RunUpRight},
        {Directions.DownRight, RunDownRight},
        {Directions.DownLeft, RunDownLeft},
    };
}