comparison xrogue/effects.c @ 133:e6179860cb76

Import XRogue 8.0 from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 21 Apr 2015 08:55:20 -0400
parents
children ce0cf824c192
comparison
equal deleted inserted replaced
124:d10fc4a065ac 133:e6179860cb76
1 /*
2 effects.c - functions for dealing with appllying effects to monsters
3
4 XRogue: Expeditions into the Dungeons of Doom
5 Copyright (C) 1991 Robert Pietkivitch
6 All rights reserved.
7
8 Based on "Advanced Rogue"
9 Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
10 All rights reserved.
11
12 Based on "Rogue: Exploring the Dungeons of Doom"
13 Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
14 All rights reserved.
15
16 See the file LICENSE.TXT for full copyright and licensing information.
17 */
18
19 #include <curses.h>
20 #include "rogue.h"
21
22 /*
23 * effect:
24 * Check for effects of one thing hitting another thing. Return
25 * the reason code if the defender is killed. Otherwise return 0.
26 */
27
28 effect(att, def, weap, thrown, see_att, see_def)
29 register struct thing *att, *def;
30 struct object *weap;
31 bool thrown;
32 register bool see_att, see_def;
33 {
34 register bool att_player, def_player;
35 char attname[LINELEN+1], defname[LINELEN+1];
36
37 /* See if the attacker or defender is the player */
38 att_player = (att == &player);
39 def_player = (def == &player);
40
41 /*
42 * If the player could see the attacker or defender, they can't
43 * surprise anymore (don't bother checking if they could).
44 */
45 if (see_att) turn_off(*att, CANSURPRISE);
46 if (see_def) turn_off(*def, CANSURPRISE);
47
48 /* What are the attacker and defender names? */
49 if (att_player) strcpy(attname, "you");
50 else {
51 if (see_att) strcpy(attname, monster_name(att));
52 else strcpy(attname, "something");
53 }
54
55 if (def_player) strcpy(defname, "you");
56 else {
57 if (see_def) strcpy(defname, monster_name(def));
58 else strcpy(defname, "something");
59 }
60
61 /*
62 * See what happens to the attacker first. We can skip this
63 * whole section, however, if the defender is the player.
64 * Nothing happens (yet) to anyone just for hitting the player.
65 */
66 if (!def_player) {
67 if (!thrown) { /* Some things require a direct hit. */
68 /*
69 * If the attacker hits a rusting monster, The weapon
70 * may be damaged
71 */
72 if (on(*def, CANRUST) && weap &&
73 weap->o_type != RELIC && (weap->o_flags & ISMETAL) &&
74 !(weap->o_flags & ISPROT)) {
75 if ((weap->o_hplus < 1 && weap->o_dplus < 1) ||
76 roll(1,20) < weap->o_hplus+weap->o_dplus+10) {
77 if (rnd(100) < 50) weap->o_hplus--;
78 else weap->o_dplus--;
79 if (att_player)
80 msg(terse ? "Your %s weakens!"
81 : "Your %s gets weaker!",
82 weaps[weap->o_which].w_name);
83 }
84 }
85 }
86
87 /* If the attacker hit something that shrieks, wake the dungeon */
88 if (on(*def, CANSHRIEK)) {
89 if (see_def)
90 msg("%s emits an ear piercing shriek! ", prname(defname, TRUE));
91 else
92 msg("You hear an ear piercing shriek!");
93
94 /* Friendly charactors should be immune */
95 if (player.t_ctype == C_PALADIN ||
96 player.t_ctype == C_RANGER || player.t_ctype == C_MONK)
97 aggravate(TRUE, FALSE);
98 else
99 aggravate(TRUE, TRUE);
100 }
101
102 /*
103 * does the creature explode when hit?
104 */
105 if (on(*def, CANEXPLODE)) {
106 if (see_def) msg("%s explodes!", prname(defname, TRUE));
107 else msg("You hear a tremendous explosion!");
108 explode(def);
109 if (pstats.s_hpt < 1) {
110 pstats.s_hpt = -1;
111 death(def->t_index);
112 }
113 }
114 }
115
116 /*
117 * Now let's see what happens to the defender. Start out with
118 * the things that everyone can do. Then exit if the attacker
119 * is the player.
120 */
121 if (!thrown) {
122 /*
123 * Can the player confuse?
124 */
125 if (on(*att, CANHUH) && att_player) {
126 msg("Your hands return to normal. ");
127 if (off(*def, ISCLEAR) &&
128 (off(*def, ISUNIQUE) || !save(VS_MAGIC, def, 0))) {
129 if (see_def) msg("%s appears confused!", prname(defname, TRUE));
130 turn_on(*def, ISHUH);
131 }
132 turn_off(*att, CANHUH);
133 }
134
135 /* Return now if the attacker is the player. */
136 if (att_player) return(0);
137
138 /*
139 * Some monsters may take half your hit points
140 */
141 if (on(*att, CANSUCK) && !save(VS_MAGIC, def, 0)) {
142 if (def->t_stats.s_hpt == 1) return(att->t_index); /* Killed! */
143 else {
144 def->t_stats.s_hpt /= 2;
145 if (def_player)
146 msg("Your life force is being drained out of you.");
147 }
148 }
149
150 /*
151 * If a hugging monster hits, it may SQUEEEEEEEZE.
152 */
153 if (on(*att, CANHUG)) {
154 if (roll(1,20) >= 18 || roll(1,20) >= 18) {
155 if (def_player)
156 msg("%s squeezes itself nastily against you!",
157 prname(attname, TRUE));
158 else if (see_att)
159 msg("%s squeezes real hard!", prname(attname, TRUE));
160
161 if ((def->t_stats.s_hpt -= roll(2,8)) <= 0)
162 return(att->t_index);
163 }
164 }
165
166 /*
167 * Some monsters have poisonous bites.
168 */
169 if (on(*att, CANPOISON) && !save(VS_POISON, def, 0)) {
170 if (def_player) {
171 if (ISWEARING(R_SUSABILITY))
172 msg(terse ? "Sting has no effect."
173 : "A sting momentarily weakens your arm.");
174 else {
175 chg_str(-1);
176 msg(terse ? "A sting has weakened you." :
177 "You get stung in the arm! You feel weaker. ");
178 }
179 }
180 else {
181 /* Subtract a strength point and see if it kills it */
182 if (--def->t_stats.s_str <= 0) return(D_STRENGTH);
183 }
184 }
185
186 /*
187 * Turning to stone:
188 */
189 if (on(*att, TOUCHSTONE)) {
190 if (def_player) turn_off(*att, TOUCHSTONE);
191 if (on(*def, CANINWALL)) {
192 if (def_player)
193 msg("%s's touch has no effect.", prname(attname, TRUE));
194 }
195 else {
196 if (!save(VS_PETRIFICATION, def, 0) && rnd(100) < 10) {
197 if (def_player) {
198 msg("Your body begins to solidify.. ");
199 msg("You are transformed into stone!! --More--");
200 wait_for(' ');
201 return(D_PETRIFY);
202 }
203 else {
204 /* The monster got stoned! */
205 turn_on(*def, ISSTONE);
206 turn_off(*def, ISRUN);
207 turn_off(*def, ISINVIS);
208 turn_off(*def, ISDISGUISE);
209 if (def->t_stats.s_intel > 15)
210 msg("%s staggers.. ", prname(defname, TRUE));
211 else if (see_def)
212 msg("%s turns to stone! ", prname(defname, TRUE));
213 else if (cansee(unc(def->t_pos)))
214 msg("A statue appears out of nowhere! ");
215 }
216 }
217 else if (def->t_action != A_FREEZE) {
218 if (def_player)
219 msg("%s's touch stiffens your limbs.",
220 prname(attname, TRUE));
221 else if (see_def)
222 msg("%s appears to freeze over.", prname(defname, TRUE));
223
224 def->t_no_move += movement(def) * STONETIME;
225 def->t_action = A_FREEZE;
226 }
227 }
228 }
229
230 /*
231 * Wraiths might drain energy levels
232 */
233 if ((on(*att, CANDRAIN) || on(*att, DOUBLEDRAIN)) &&
234 !save(VS_POISON, def, 3-(att->t_stats.s_lvl/5))) {
235 if (def_player) {
236 lower_level(att->t_index);
237 if (on(*att, DOUBLEDRAIN)) lower_level(att->t_index);
238 turn_on(*att, DIDDRAIN);
239 }
240 else {
241 def->t_stats.s_hpt -= roll(1, 8);
242 def->t_stats.s_lvl--;
243 if (on(*att, DOUBLEDRAIN)) {
244 def->t_stats.s_hpt -= roll(1, 8);
245 def->t_stats.s_lvl--;
246 }
247 if (see_def)
248 msg("%s appears less skillful.", prname(defname, TRUE));
249
250 /* Did it kill it? */
251 if (def->t_stats.s_hpt <= 0 ||
252 def->t_stats.s_lvl <= 0)
253 return(att->t_index);
254 }
255 }
256
257 /*
258 * Paralyzation:
259 */
260 if (on(*att, CANPARALYZE) && def->t_action != A_FREEZE) {
261 if (def_player) turn_off(*att, CANPARALYZE);
262 if (!save(VS_PARALYZATION, def, 0)) {
263 if (on(*def, CANINWALL)) {
264 if (def_player)
265 msg("%s's touch has no effect.", prname(attname, TRUE));
266 }
267 else {
268 if (def_player)
269 msg("%s's touch paralyzes you.", prname(attname, TRUE));
270 else if (see_def)
271 msg("%s appears to freeze over!", prname(defname, TRUE));
272
273 def->t_no_move += movement(def) * FREEZETIME;
274 def->t_action = A_FREEZE;
275 }
276 }
277 }
278
279 /*
280 * Painful wounds make the defendant faint
281 */
282 if (on(*att, CANPAIN) && def->t_action != A_FREEZE) {
283 if (def_player) turn_off(*att, CANPAIN);
284 if (!ISWEARING(R_ALERT) && !save(VS_POISON, def, 0)) {
285 if (def_player)
286 msg("You faint from the painful wound!");
287 else if (see_def)
288 msg("%s appears to faint!", prname(defname, TRUE));
289
290 def->t_no_move += movement(def) * PAINTIME;
291 def->t_action = A_FREEZE;
292 }
293 }
294
295 /*
296 * Some things currently affect only the player. Let's make
297 * a check here so we don't have to check for each thing.
298 */
299 if (def_player) {
300 /*
301 * Stinking monsters make the defender weaker (to hit). For now
302 * this will only affect the player. We may later add the HASSTINK
303 * effect to monsters, too.
304 */
305 if (on(*att, CANSTINK)) {
306 turn_off(*att, CANSTINK);
307 if (!save(VS_POISON, def, 0)) {
308 msg("The stench of %s sickens you. Blech!",
309 prname(attname, FALSE));
310 if (on(player, HASSTINK)) lengthen(unstink, STINKTIME);
311 else {
312 turn_on(player, HASSTINK);
313 fuse(unstink, (VOID *)NULL, STINKTIME, AFTER);
314 }
315 }
316 }
317
318 /*
319 * Chilling monster reduces strength each time. This only
320 * affects the player for now because of its temporary nature.
321 */
322 if (on(*att, CANCHILL)) {
323 if (!ISWEARING(R_SUSABILITY) && !save(VS_POISON, def, 0)) {
324 msg("You cringe at %s's chilling touch.",
325 prname(attname, FALSE));
326 chg_str(-1);
327 if (lost_str++ == 0)
328 fuse(res_strength, (VOID *)NULL, CHILLTIME, AFTER);
329 else lengthen(res_strength, CHILLTIME);
330 }
331 }
332
333 /*
334 * Itching monsters reduce dexterity (temporarily). This only
335 * affects the player for now because of its temporary nature.
336 */
337 if (on(*att, CANITCH) && !save(VS_POISON, def, 0)) {
338 msg("The claws of %s scratch you!", prname(attname, FALSE));
339 if (ISWEARING(R_SUSABILITY)) {
340 msg("The scratch has no effect.");
341 }
342 else {
343 (*add_abil[A_DEXTERITY])(-1);
344 }
345 }
346
347 /*
348 * If a disease-carrying monster hits, there is a chance the
349 * defender will catch the disease. This only applies to the
350 * player for now because of the temporary nature. Don't affect
351 * the Ranger or Paladin.
352 */
353 if (on(*att, CANDISEASE) &&
354 (rnd(def->t_stats.s_const) < att->t_stats.s_lvl) &&
355 off(*def, HASDISEASE)) {
356 if (ISWEARING(R_HEALTH) ||
357 player.t_ctype == C_PALADIN ||
358 player.t_ctype == C_RANGER) {
359 msg("The wound heals quickly.");
360 }
361 else {
362 turn_on(*def, HASDISEASE);
363 fuse(cure_disease, (VOID *)NULL, roll(HEALTIME,SICKTIME), AFTER);
364 msg(terse ? "You have been diseased!"
365 : "You have contracted an annoying disease!");
366 }
367 }
368
369 /*
370 * If a rusting monster hits, you lose armor. This only applies to
371 * the player because monsters don't wear armor (for now).
372 */
373 if (on(*att, CANRUST)) {
374 if (cur_armor != NULL &&
375 cur_armor->o_which != LEATHER &&
376 cur_armor->o_which != STUDDED_LEATHER &&
377 cur_armor->o_which != PADDED_ARMOR &&
378 !(cur_armor->o_flags & ISPROT) &&
379 cur_armor->o_ac < def->t_stats.s_arm+1) {
380 msg(terse ? "Your armor weakens."
381 : "Your armor becomes weaker.");
382 cur_armor->o_ac++;
383 }
384 if (cur_misc[WEAR_BRACERS] != NULL &&
385 cur_misc[WEAR_BRACERS]->o_ac > 0 &&
386 !(cur_misc[WEAR_BRACERS]->o_flags & ISPROT)) {
387 cur_misc[WEAR_BRACERS]->o_ac--;
388 if (cur_misc[WEAR_BRACERS]->o_ac == 0) {
389 register struct linked_list *item;
390
391 for (item=pack; item!=NULL; item=next(item)) {
392 if (OBJPTR(item) == cur_misc[WEAR_BRACERS]) {
393 detach(pack, item);
394 o_discard(item);
395 break;
396 }
397 }
398 msg ("Your bracers crumble apart!");
399 cur_misc[WEAR_BRACERS] = NULL;
400 inpack--;
401 }
402 else {
403 msg("Your bracers weaken!");
404 }
405 }
406 }
407
408 /*
409 * If can dissolve and hero has leather type armor. This
410 * also only applies to the player for now because of the
411 * armor.
412 */
413 if (on(*att, CANDISSOLVE) && cur_armor != NULL &&
414 (cur_armor->o_which == LEATHER ||
415 cur_armor->o_which == STUDDED_LEATHER ||
416 cur_armor->o_which == PADDED_ARMOR) &&
417 !(cur_armor->o_flags & ISPROT) &&
418 cur_armor->o_ac < def->t_stats.s_arm+1) {
419 msg(terse ? "Your armor dissolves!"
420 : "Your armor appears to have dissolved!");
421 cur_armor->o_ac++;
422 }
423
424 /*
425 * If an infesting monster hits you, you get a parasite or rot.
426 * This will only affect the player until we figure out how to
427 * make it affect monsters. Don't affect the Monk.
428 */
429 if (on(*att, CANINFEST) &&
430 rnd(def->t_stats.s_const) < att->t_stats.s_lvl) {
431 if (ISWEARING(R_HEALTH) || player.t_ctype == C_MONK) {
432 msg("The wound heals quickly.");
433 }
434 else {
435 turn_off(*att, CANINFEST);
436 msg(terse ? "You have been infested."
437 : "You have contracted a parasitic infestation!");
438 infest_dam++;
439 turn_on(*def, HASINFEST);
440 }
441 }
442
443 /*
444 * Does it take wisdom away? This currently affects only
445 * the player because of its temporary nature.
446 */
447 if (on(*att, TAKEWISDOM) &&
448 !save(VS_MAGIC, def, 0) &&
449 !ISWEARING(R_SUSABILITY)) {
450 (*add_abil[A_WISDOM])(-1);
451 }
452
453 /*
454 * Does it take intelligence away? This currently affects
455 * only the player because of its temporary nature.
456 */
457 if (on(*att, TAKEINTEL) &&
458 !save(VS_MAGIC, &player, 0) &&
459 !ISWEARING(R_SUSABILITY)) {
460 (*add_abil[A_INTELLIGENCE])(-1);
461 }
462
463 /*
464 * Cause fear by touching. This currently affects only
465 * the player until we figure out how we want it to
466 * affect monsters.
467 */
468 if (on(*att, TOUCHFEAR)) {
469 turn_off(*att, TOUCHFEAR);
470 if (!ISWEARING(R_HEROISM) &&
471 !save(VS_WAND, def, 0) &&
472 !(on(*def, ISFLEE) && (def->t_dest == &att->t_pos))) {
473 turn_on(*def, ISFLEE);
474 def->t_dest = &att->t_pos;
475 msg("%s's touch terrifies you!", prname(attname, TRUE));
476
477 /* It is okay to turn tail */
478 if (!def_player) def->t_oldpos = def->t_pos;
479 }
480 }
481
482 /*
483 * Make the hero dance (as in otto's irresistable dance)
484 * This should be fairly easy to do to monsters, but
485 * we'll restrict it to players until we decide what to
486 * do about the temporary nature.
487 */
488 if (on(*att, CANDANCE) &&
489 !on(*def, ISDANCE) &&
490 def->t_action != A_FREEZE &&
491 !save(VS_MAGIC, def, -4)) {
492 turn_off(*att, CANDANCE);
493 turn_on(*def, ISDANCE);
494 msg("You begin to dance uncontrollably!");
495 fuse(undance, (VOID *)NULL, roll(2,4), AFTER);
496 }
497
498 /*
499 * Suffocating our hero. Monsters don't get suffocated.
500 * That's too hard for now.
501 */
502 if (on(*att, CANSUFFOCATE) &&
503 !ISWEARING(R_FREEDOM) &&
504 rnd(100) < 30 &&
505 (find_slot(suffocate) == 0)) {
506 turn_on(*att, DIDSUFFOCATE);
507 msg("%s is beginning to suffocate you!", prname(attname, TRUE));
508 fuse(suffocate, (VOID *)NULL, roll(9,3), AFTER);
509 }
510
511 /*
512 * some creatures stops the poor guy from moving.
513 * How can we do this to a monster?
514 */
515 if (on(*att,CANHOLD) && off(*att,DIDHOLD) && !ISWEARING(R_FREEDOM)){
516 turn_on(*def, ISHELD);
517 turn_on(*att, DIDHOLD);
518 hold_count++;
519 }
520
521 /*
522 * Sucker will suck blood and run. This
523 * should be easy to have happen to a monster,
524 * but we have to decide how to handle the fleeing.
525 */
526 if (on(*att, CANDRAW)) {
527 turn_off(*att, CANDRAW);
528 turn_on(*att, ISFLEE);
529 msg("%s sates itself with your blood!", prname(attname, TRUE));
530 if ((def->t_stats.s_hpt -= 12) <= 0) return(att->t_index);
531
532 /* It is okay to turn tail */
533 att->t_oldpos = att->t_pos;
534 }
535
536 /*
537 * Bad smell will force a reduction in strength.
538 * This will happen only to the player because of
539 * the temporary nature.
540 */
541 if (on(*att, CANSMELL)) {
542 turn_off(*att, CANSMELL);
543 if (save(VS_MAGIC, def, 0) || ISWEARING(R_SUSABILITY)) {
544 if (terse)
545 msg("Pheww!");
546 else
547 msg("You smell an unpleasant odor. Phew!");
548 }
549
550 else {
551 int odor_str = -(rnd(6)+1);
552
553 msg("You are overcome by a foul odor!");
554 if (lost_str == 0) {
555 chg_str(odor_str);
556 fuse(res_strength, (VOID *)NULL, SMELLTIME, AFTER);
557 lost_str -= odor_str;
558 }
559 else lengthen(res_strength, SMELLTIME);
560 }
561 }
562
563 /*
564 * The monsters touch slows the defendant down.
565 */
566 if (on(*att, TOUCHSLOW)) {
567 turn_off(*att, TOUCHSLOW);
568 if (!save(VS_PARALYZATION, def, 0))
569 add_slow();
570 }
571
572 /*
573 * Rotting only affects the player. Don't affect the Monk,
574 * Paladin, or Ranger.
575 */
576 if (on(*att, CANROT)) {
577 if (!ISWEARING(R_HEALTH) &&
578 player.t_ctype != C_MONK &&
579 player.t_ctype != C_RANGER &&
580 player.t_ctype != C_PALADIN &&
581 !save(VS_POISON, def, 0) &&
582 off(*def, DOROT)) {
583 turn_on(*def, DOROT);
584 msg("You feel your skin starting to rot and peel away!");
585 }
586 }
587
588 /*
589 * Monsters should be able to steal gold from anyone,
590 * but until this is rewritten, they will only steal
591 * from the player (tough break).
592 */
593 if (on(*att, STEALGOLD)) {
594 /*
595 * steal some gold
596 */
597 register long lastpurse;
598 register struct linked_list *item;
599 register struct object *obj;
600
601 lastpurse = purse;
602 purse -= (GOLDCALC * 2);
603 if (!save(VS_MAGIC, def, att->t_stats.s_lvl/10)) {
604 if (on(*att, ISUNIQUE))
605 purse -= (GOLDCALC * 5);
606 else
607 purse -= (GOLDCALC * 3);
608 }
609 if (purse < 0)
610 purse = 0;
611 if (purse != lastpurse) {
612 msg("You lost some gold! ");
613
614 /* Give the gold to the thief */
615 for (item=att->t_pack; item != NULL; item=next(item)) {
616 obj = OBJPTR(item);
617 if (obj->o_type == GOLD) {
618 obj->o_count += lastpurse - purse;
619 break;
620 }
621 }
622
623 /* Did we do it? */
624 if (item == NULL) { /* Then make some */
625 item = new_item(sizeof *obj);
626 obj = OBJPTR(item);
627 obj->o_type = GOLD;
628 obj->o_count = lastpurse - purse;
629 obj->o_hplus = obj->o_dplus = 0;
630 strcpy(obj->o_damage,"0d0");
631 strcpy(obj->o_hurldmg,"0d0");
632 obj->o_ac = 11;
633 obj->contents = NULL;
634 obj->o_group = 0;
635 obj->o_flags = 0;
636 obj->o_mark[0] = '\0';
637 obj->o_pos = att->t_pos;
638
639 attach(att->t_pack, item);
640 }
641 }
642
643 turn_on(*att, ISFLEE);
644 turn_on(*att, ISINVIS);
645
646 /* It is okay to turn tail */
647 att->t_oldpos = att->t_pos;
648 }
649 }
650
651 /*
652 * Stealing happens last since the monster disappears
653 * after the act.
654 */
655 if (on(*att, STEALMAGIC)) {
656 register struct linked_list *list, *steal;
657 register struct object *obj;
658 register int nobj;
659
660 /*
661 * steal a magic item, look through the pack
662 * and pick out one we like.
663 */
664 steal = NULL;
665 for (nobj = 0, list = def->t_pack; list != NULL; list = next(list))
666 {
667 obj = OBJPTR(list);
668 if (!is_current(obj) &&
669 list != def->t_using &&
670 obj->o_type != RELIC &&
671 is_magic(obj) &&
672 rnd(++nobj) == 0)
673 steal = list;
674 }
675 if (steal != NULL)
676 {
677 register struct object *obj;
678 struct linked_list *item;
679
680 obj = OBJPTR(steal);
681 if (on(*att, ISUNIQUE))
682 monsters[att->t_index].m_normal = TRUE;
683 item = find_mons(att->t_pos.y, att->t_pos.x);
684
685 killed(item, FALSE, FALSE, FALSE); /* Remove the attacker */
686
687 if (obj->o_count > 1 && obj->o_group == 0) {
688 register int oc;
689
690 oc = --(obj->o_count);
691 obj->o_count = 1;
692 if (def_player)
693 msg("%s stole %s!", prname(attname, TRUE),
694 inv_name(obj, TRUE));
695 obj->o_count = oc;
696 }
697 else {
698 if (def_player) {
699 msg("%s stole %s!", prname(attname, TRUE),
700 inv_name(obj, TRUE));
701
702 /* If this is a relic, clear its holding field */
703 if (obj->o_type == RELIC)
704 cur_relic[obj->o_which] = 0;
705
706 inpack--;
707 }
708
709 detach(def->t_pack, steal);
710 o_discard(steal);
711 }
712
713 updpack(FALSE, def);
714 }
715 }
716 }
717
718 /* Didn't kill the defender */
719 return(0);
720 }
721