Line data Source code
1 : #include "mobsystem.h"
2 :
3 : #include "game.h"
4 : #include "rendersystem.h"
5 :
6 : #include <string>
7 : using namespace std::string_literals;
8 :
9 3 : MobInfo MI(MobCategory category, std::string name, int32_t health, int32_t speed) {
10 3 : MobInfo mi;
11 3 : mi.category = category;
12 3 : mi.name = name;
13 3 : mi.health = health;
14 3 : mi.speed = speed;
15 3 : return mi;
16 : }
17 :
18 4 : MobInfo MI(MobCategory category, std::string name, int32_t health, int32_t speed,
19 : int32_t strength) {
20 4 : MobInfo mi;
21 4 : mi.category = category;
22 4 : mi.name = name;
23 4 : mi.health = health;
24 4 : mi.speed = speed;
25 4 : mi.attacks = true;
26 4 : mi.strength = strength;
27 4 : return mi;
28 : }
29 :
30 1 : const std::unordered_map<MobType, MobInfo> MobDatabase{
31 2 : {MobType::Unknown, MI(MobCategory::Unknown, "Unknown"s, 0, 1)},
32 2 : {MobType::Rabbit, MI(MobCategory::Rabbit, "Rabbit"s, 1, 7)},
33 2 : {MobType::RabbitWere, MI(MobCategory::Rabbit, "Were-Rabbit"s, 1, 6, 1)},
34 2 : {MobType::Snake, MI(MobCategory::Snake, "Snake"s, 1, 5)},
35 2 : {MobType::OrcWeak, MI(MobCategory::Orc, "Little Orc"s, 5, 3, 3)},
36 2 : {MobType::OrcStrong, MI(MobCategory::Orc, "Big Orc"s, 6, 2, 5)},
37 2 : {MobType::Player, MI(MobCategory::Player, "Player"s, 5, 6, 3)},
38 15 : };
39 :
40 5 : void MobSystem::update() {
41 205 : for (auto& mob : game_.mobs.values()) {
42 200 : if (mob.info->category == MobCategory::Player)
43 5 : continue;
44 :
45 195 : mob.tick += mob.info->speed;
46 195 : mob.tick = std::min(mob.tick, 2 * Mob::TicksPerAction - 1);
47 195 : if (mob.tick >= Mob::TicksPerAction) {
48 0 : auto& e = game_.entities[mob.entity];
49 0 : updateMob(e, mob);
50 0 : mob.tick -= Mob::TicksPerAction;
51 : }
52 : }
53 5 : }
54 :
55 0 : void MobSystem::handleEvent(const EvAny& any) {
56 0 : if (any.is<EvTryWalk>()) {
57 0 : const auto& ev = any.get<EvTryWalk>();
58 0 : auto& mob = game_.mobs[ev.mob];
59 0 : auto& info = *mob.info;
60 :
61 : // check position is clear
62 0 : bool blocked = false;
63 0 : for (auto& other : game_.mobs.values()) {
64 0 : if (other.id != mob.id && other.position == ev.to) {
65 0 : blocked = true;
66 0 : break;
67 : }
68 : }
69 :
70 0 : if (!game_.worldBounds.contains(ev.to)) {
71 0 : blocked = true;
72 : }
73 :
74 0 : if (!blocked) {
75 0 : mob.position = ev.to;
76 :
77 : // Mob overrides sprite position
78 0 : auto& sprite = game_.sprites[game_.entities[mob.entity].sprite];
79 0 : sprite.position = mob.position;
80 :
81 : // Additional pieces
82 0 : switch (info.category) {
83 0 : default:
84 0 : break;
85 0 : case MobCategory::Snake: {
86 0 : if (randInt(0, 3) < 3) {
87 0 : game_.groundTile(mob.position) = '_';
88 : }
89 :
90 0 : game_.sprites[mob.extraSprite].position = mob.position + mob.dir;
91 0 : break;
92 : }
93 0 : case MobCategory::Orc: {
94 0 : if (randInt(0, 1) == 0) {
95 : // smash ground
96 0 : game_.groundTile(mob.position) = '_';
97 : }
98 0 : game_.sprites[mob.extraSprite].position = mob.position + vec2i{-1, 1};
99 0 : game_.sprites[mob.extraSprite2].position = mob.position + vec2i{1, 1};
100 0 : break;
101 : }
102 : }
103 :
104 0 : game_.queueEvent(EvWalked{mob.id, ev.from, mob.position});
105 : }
106 0 : } else if (any.is<EvWalked>()) {
107 : // ...
108 0 : } else if (any.is<EvAttack>()) {
109 0 : const auto& ev = any.get<EvAttack>();
110 0 : auto& mob = game_.mobs[ev.mob];
111 0 : auto& mobInfo = *mob.info;
112 0 : auto& targetMob = game_.mobs[ev.target];
113 : // auto& targetMobInfo = *targetMob.info;
114 :
115 0 : if (mob && targetMob) {
116 0 : targetMob.health -= mobInfo.strength;
117 0 : if (targetMob.health <= 0) {
118 0 : game_.queueEvent(EvKillMob{targetMob.id});
119 : } else {
120 : // Flash-hit
121 0 : const int flashDuration = 2;
122 0 : auto& e = game_.entities[targetMob.entity];
123 0 : game_.sprites[e.sprite].flashTimer = flashDuration;
124 0 : if (targetMob.extraSprite)
125 0 : game_.sprites[targetMob.extraSprite].flashTimer = flashDuration;
126 0 : if (targetMob.extraSprite2)
127 0 : game_.sprites[targetMob.extraSprite2].flashTimer = flashDuration;
128 : }
129 : }
130 : }
131 0 : }
132 :
133 0 : void MobSystem::updateMob(Entity& e, Mob& mob) {
134 0 : auto& info = *mob.info;
135 0 : auto& sprite = game_.sprites[e.sprite];
136 0 : vec2i pos = mob.position;
137 :
138 0 : const int margin = 6; // min distance from edge mobs prefer to be
139 0 : auto dirToNearestEdge = [&](vec2i pos) -> vec2i {
140 0 : const auto& b = game_.worldBounds;
141 :
142 0 : if (pos.y > b.top - margin)
143 0 : return {0, -1};
144 0 : else if (pos.y < b.top - b.height + margin)
145 0 : return {0, 1};
146 0 : else if (pos.x < b.left + margin)
147 0 : return {1, 0};
148 0 : else if (pos.x > b.left + b.width - margin)
149 0 : return {-1, 0};
150 0 : return {0, 0};
151 0 : };
152 :
153 0 : switch (info.category) {
154 0 : case MobCategory::Rabbit: {
155 0 : if (randInt(0, 500) == 0) {
156 : // Too many rabbits!
157 : // game_.queueEvent(EvSpawnMob { MobType::Rabbit, pos });
158 : } else {
159 : // Move randomly
160 0 : vec2i dir = dirToNearestEdge(pos);
161 0 : if (dir == vec2i{0, 0}) {
162 0 : dir = vec2i{randInt(-1, 1), randInt(-1, 1)};
163 : }
164 0 : game_.queueEvent(EvTryWalk{mob.id, pos, pos + dir});
165 : }
166 0 : break;
167 : }
168 0 : case MobCategory::Snake: {
169 0 : if (randInt(0, 6) == 0) {
170 0 : if (mob.dir.x != 0) {
171 0 : mob.dir = choose<vec2i>({{0, 1}, {0, -1}});
172 : } else {
173 0 : mob.dir = choose<vec2i>({{1, 0}, {-1, 0}});
174 : }
175 :
176 0 : vec2i dir = dirToNearestEdge(pos);
177 0 : if (dir != vec2i{0, 0})
178 0 : mob.dir = dir;
179 : } else {
180 0 : if (mob.dir.y == 1)
181 0 : sprite.frame = 0;
182 0 : else if (mob.dir.y == -1)
183 0 : sprite.frame = 1;
184 0 : else if (mob.dir.x == 1)
185 0 : sprite.frame = 2;
186 0 : else if (mob.dir.x == -1)
187 0 : sprite.frame = 3;
188 :
189 0 : game_.queueEvent(EvTryWalk{mob.id, pos, pos + mob.dir});
190 : }
191 0 : break;
192 : }
193 0 : case MobCategory::Orc: {
194 0 : if (randInt(0, 2) == 0) {
195 0 : game_.groundTile(pos) = choose({'_', '_'});
196 : }
197 :
198 0 : vec2i dir = dirToNearestEdge(pos);
199 0 : if (dir == vec2i{0, 0}) {
200 0 : if (randInt(0, 3) == 0) {
201 : // stay here
202 : } else {
203 : // move randomly
204 0 : int32_t move = choose({-1, 1});
205 0 : dir = choose({vec2i{move, 0}, vec2i{0, move}});
206 : }
207 : }
208 :
209 0 : if (dir != vec2i{0, 0}) {
210 0 : game_.queueEvent(EvTryWalk{mob.id, pos, pos + dir});
211 : }
212 0 : break;
213 : }
214 0 : default:
215 0 : break;
216 : }
217 3 : }
|