1 /*
2 * spheres.c
3 * Copyright (C) 2009-2018 Joachim de Groot <jdegroot@web.de>
4 *
5 * NLarn is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * NLarn is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <glib.h>
20
21 #include "game.h"
22 #include "extdefs.h"
23 #include "random.h"
24 #include "spheres.h"
25
26 static sphere *sphere_at(game *g, position pos, sphere *s);
27 static void sphere_hit_owner(game *g, sphere *s);
28 static void sphere_kill_monster(sphere *s, monster *m);
29
sphere_new(position pos,player * owner,int lifetime)30 sphere *sphere_new(position pos, player *owner, int lifetime)
31 {
32 sphere *s;
33
34 s = g_malloc0(sizeof(sphere));
35
36 s->pos = pos;
37 s->owner = owner;
38
39 s->dir = rand_1n(GD_MAX);
40
41 /* prevent spheres hanging around */
42 if (s->dir == GD_CURR)
43 s->dir++;
44
45 s->lifetime = lifetime;
46
47 return s;
48 }
49
sphere_destroy(sphere * s,game * g)50 void sphere_destroy(sphere *s, game *g)
51 {
52 g_assert(s != NULL);
53
54 g_ptr_array_remove_fast(g->spheres, s);
55 g_free(s);
56 }
57
sphere_serialize(sphere * s,cJSON * root)58 void sphere_serialize(sphere *s, cJSON *root)
59 {
60 cJSON *sval;
61
62 cJSON_AddItemToArray(root, sval = cJSON_CreateObject());
63
64 cJSON_AddNumberToObject(sval, "dir", s->dir);
65 cJSON_AddNumberToObject(sval, "lifetime", s->lifetime);
66 cJSON_AddNumberToObject(sval, "pos", pos_val(s->pos));
67
68 if (!s->owner)
69 cJSON_AddFalseToObject(sval, "owner");
70 }
71
sphere_deserialize(cJSON * sser,game * g)72 void sphere_deserialize(cJSON *sser, game *g)
73 {
74 sphere *s = g_malloc0(sizeof(sphere));
75
76 s->dir = cJSON_GetObjectItem(sser, "dir")->valueint;
77 s->lifetime = cJSON_GetObjectItem(sser, "lifetime")->valueint;
78 pos_val(s->pos) = cJSON_GetObjectItem(sser, "pos")->valueint;
79
80 if (!cJSON_GetObjectItem(sser, "owner"))
81 s->owner = g->p;
82
83 g_ptr_array_add(g->spheres, s);
84 }
85
sphere_move(sphere * s,game * g)86 void sphere_move(sphere *s, game *g)
87 {
88 position npos;
89 int tries = 0;
90 direction dir;
91 monster *m;
92 map *smap;
93
94 g_assert(s != NULL && g != NULL);
95
96 /* reduce lifetime */
97 s->lifetime--;
98
99 /* sphere has reached end of life */
100 if (s->lifetime == 0)
101 {
102 sphere_destroy(s, g);
103 return;
104 }
105
106 smap = game_map(g, Z(s->pos));
107
108 /* try to move sphere into its direction */
109 dir = s->dir;
110 npos = pos_move(s->pos, dir);
111
112 /* if the new position does not work, try to find another one */
113 while ((!pos_valid(npos) || !map_pos_passable(smap, npos))
114 && (tries < GD_MAX))
115 {
116 dir++;
117
118 if (dir == GD_CURR)
119 dir++;
120
121 if (dir == GD_MAX)
122 dir = 1;
123
124 npos = pos_move(s->pos, dir);
125 tries++;
126 }
127
128 /* new position has been found, save it and the direction */
129 if (tries < GD_MAX)
130 {
131 s->dir = dir;
132 s->pos = npos;
133 }
134 /* otherwise stand still */
135
136 /* sphere rolled over it's creator */
137 if (pos_identical(s->pos, s->owner->pos))
138 {
139 sphere_hit_owner(g, s);
140 return;
141 }
142
143 /* check if a monster is located at the sphere's position */
144 if ((m = map_get_monster_at(smap, s->pos)))
145 {
146 /* demons dispel spheres */
147 if (monster_flags(m, DEMON))
148 {
149 if (monster_in_sight(m))
150 {
151 log_add_entry(nlarn->log, "The %s dispels the sphere!",
152 monster_name(m));
153 }
154
155 sphere_destroy(s, g);
156
157 return;
158 }
159
160 /* disenchantress cancels sphere */
161 if (monster_type(m) == MT_DISENCHANTRESS)
162 {
163 if (monster_in_sight(m))
164 {
165 log_add_entry(nlarn->log,
166 "The %s causes cancellation of the sphere!",
167 monster_name(m));
168 }
169
170 sphere_destroy(s, g);
171
172 return;
173 }
174
175 /* kill monster */
176 sphere_kill_monster(s, m);
177
178 return;
179 }
180
181 /* check if another sphere is located at the same position */
182 sphere *other;
183 if ((other = sphere_at(g, s->pos, s)))
184 {
185 if (fov_get(g->p->fv, s->pos))
186 {
187 log_add_entry(nlarn->log,
188 "Two spheres of annihilation collide! "
189 "You hear a great earth shaking blast!");
190 }
191 else if(Z(g->p->pos) == Z(s->pos))
192 {
193 /* The player is on the same level as the spheres */
194 log_add_entry(nlarn->log,
195 "You hear a great earth shaking blast!");
196 }
197
198 sphere_destroy(s, g);
199 sphere_destroy(other, g);
200 }
201 }
202
203
sphere_at(game * g,position pos,sphere * s)204 static sphere *sphere_at(game *g, position pos, sphere *s)
205 {
206 for (guint idx = 0; idx < g->spheres->len; idx++)
207 {
208 sphere *cs = (sphere *)g_ptr_array_index(g->spheres, idx);
209
210 if (s != cs && pos_identical(cs->pos, pos))
211 return cs;
212 }
213
214 return NULL;
215 }
216
sphere_hit_owner(game * g,sphere * s)217 static void sphere_hit_owner(game *g, sphere *s)
218 {
219 log_add_entry(nlarn->log, "You are hit by a sphere of annihilation!");
220
221 if (player_effect(s->owner, ET_CANCELLATION))
222 {
223 /* cancellation protects from spheres */
224 log_add_entry(nlarn->log,
225 "As the cancellation takes effect, you "
226 "hear a great earth shaking blast!");
227
228 sphere_destroy(s, g);
229
230 effect *e = player_effect_get(g->p, ET_CANCELLATION);
231 player_effect_del(g->p, e);
232 }
233 else
234 {
235 player_die(s->owner, PD_SPHERE, 0);
236 }
237 }
238
sphere_kill_monster(sphere * s,monster * m)239 static void sphere_kill_monster(sphere *s, monster *m)
240 {
241 monster *mret; /* monster returned by monster_damage_take */
242 guint mexp; /* xp for killing the monster */
243
244 g_assert(s != NULL && m != NULL);
245
246 mexp = monster_exp(m);
247
248 if (monster_in_sight(m))
249 {
250 log_add_entry(nlarn->log,
251 "The sphere of annihilation hits the %s.",
252 monster_name(m));
253 }
254
255 mret = monster_damage_take(m, damage_new(DAM_MAGICAL, ATT_MAGIC, 2000, DAMO_SPHERE, s));
256
257 if (!mret && s->owner)
258 {
259 /* the monster has been killed, grant experience */
260 player_exp_gain(s->owner, mexp);
261 }
262 }
263