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