Skip to content

Commit c4d6db6

Browse files
committed
Crafting traps, part 1: initial implementation.
With this commit, it is now possible for the player to build their own traps using a new object called a 'trap kit'. A trap kit comes with 10-30 'charges', and Rogue's will always start the game with one in their inventory. These kits can spawn randomly as well, but are very rare. These can be 'recharged' with a scroll of charging as many times as needed. The charges simulate the materials and tools needed to build a trap, much like the charges in a tinning kit simulate the number of tins available to preserve corpses. The initial implementation, only arrow traps can be built for now - there's a LOT more to do here, but this commit basically sets the framework, and shows that this actually works. How it works: the player applies their trap kit, they're asked to supply a component. If the correct component (type and quantity) is provided, a trap is created and will appear in inventory (in this case, an 'arrow trap'). The player can then apply the newly created trap to activate it on the tile space they are standing on, same rules apply as with setting a bear trap or a land mine. Because of the changes made here, if the player were to wish for an arrow trap, they'll actually receive an arrow trap in inventory that they can use. In wizard mode, typically you'd wish for 'arrow trap' to make a set trap on the tile space you're standing on - now you'd use 'arrow trap set' instead to create a set trap on the current tile space. Traps I had in mind that could actually be crafted: arrow traps (implemented), land mines, bear traps, bolt traps, dart traps, sleeping gas traps, rust traps, fire traps, ice traps, teleportation traps (not levelport), magic traps, antimagic traps, polymorph traps, spear traps, magic beam traps, squeaky board traps, and falling rock traps. Trap doors, maybe? Turning a pit into a spiked pit, also maybe? Would need to figure out suitable components, and in what quantities. Case in point - right now, it only takes 20 arrows to craft an arrow trap, but depending on your luck, disarming it could yield twice as many arrows, or more. Will need to think on that. Other to-do's - create a new skill for building/setting/disarming traps. All traps could be possible to create, but the more advanced traps (poly traps, magic beam traps, etc) would require higher skill to ensure a decent chance of success. Then figure out which roles should have access to this skill (some are obvious choice, others not so much). Also have a look at xNetHack's trap ammo revamp, this would allow using a specific material type for a component to create a trap using that material (use silver arrows to make an arrow trap, it'll fire silver arrows).
1 parent 89fcebe commit c4d6db6

File tree

16 files changed

+279
-157
lines changed

16 files changed

+279
-157
lines changed

doc/evilhack-changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4378,4 +4378,5 @@ The following changes to date are:
43784378
- Initial preparation for new version (0.9.1)
43794379
- Properly display fractured altars in dungeon overview
43804380
- Druids can pass directly through trees
4381+
- Crafting traps, part 1: initial implementation
43814382

include/trap.h

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -55,47 +55,47 @@ extern struct trap *ftrap;
5555

5656
/* unconditional traps */
5757
enum trap_types {
58-
NO_TRAP = 0,
59-
ARROW_TRAP = 1,
60-
BOLT_TRAP = 2,
61-
DART_TRAP = 3,
62-
ROCKTRAP = 4,
63-
SQKY_BOARD = 5,
64-
BEAR_TRAP = 6,
65-
LANDMINE = 7,
66-
ROLLING_BOULDER_TRAP = 8,
67-
SLP_GAS_TRAP = 9,
68-
RUST_TRAP = 10,
69-
FIRE_TRAP = 11,
70-
ICE_TRAP = 12,
71-
PIT = 13,
72-
SPIKED_PIT = 14,
73-
HOLE = 15,
74-
TRAPDOOR = 16,
75-
TELEP_TRAP = 17,
76-
LEVEL_TELEP = 18,
77-
MAGIC_PORTAL = 19,
78-
WEB = 20,
79-
STATUE_TRAP = 21,
80-
MAGIC_TRAP = 22,
81-
ANTI_MAGIC = 23,
82-
POLY_TRAP = 24,
83-
SPEAR_TRAP = 25,
84-
MAGIC_BEAM_TRAP = 26,
85-
VIBRATING_SQUARE = 27,
58+
NO_TRAP = 0,
59+
ARROW_TRAP_SET = 1,
60+
BOLT_TRAP_SET = 2,
61+
DART_TRAP_SET = 3,
62+
ROCKTRAP = 4,
63+
SQKY_BOARD = 5,
64+
BEAR_TRAP = 6,
65+
LANDMINE = 7,
66+
ROLLING_BOULDER_TRAP = 8,
67+
SLP_GAS_TRAP_SET = 9,
68+
RUST_TRAP_SET = 10,
69+
FIRE_TRAP_SET = 11,
70+
ICE_TRAP_SET = 12,
71+
PIT = 13,
72+
SPIKED_PIT = 14,
73+
HOLE = 15,
74+
TRAPDOOR = 16,
75+
TELEP_TRAP_SET = 17,
76+
LEVEL_TELEP = 18,
77+
MAGIC_PORTAL = 19,
78+
WEB = 20,
79+
STATUE_TRAP = 21,
80+
MAGIC_TRAP_SET = 22,
81+
ANTI_MAGIC = 23,
82+
POLY_TRAP_SET = 24,
83+
SPEAR_TRAP_SET = 25,
84+
MAGIC_BEAM_TRAP_SET = 26,
85+
VIBRATING_SQUARE = 27,
8686

87-
TRAPNUM = 28
87+
TRAPNUM = 28
8888
};
8989

9090
#define is_pit(ttyp) ((ttyp) == PIT || (ttyp) == SPIKED_PIT)
9191
#define is_hole(ttyp) ((ttyp) == HOLE || (ttyp) == TRAPDOOR)
9292
#define undestroyable_trap(ttyp) ((ttyp) == MAGIC_PORTAL \
9393
|| (ttyp) == VIBRATING_SQUARE)
94-
#define is_magical_trap(ttyp) ((ttyp) == TELEP_TRAP \
95-
|| (ttyp) == LEVEL_TELEP \
96-
|| (ttyp) == MAGIC_TRAP \
97-
|| (ttyp) == ANTI_MAGIC \
98-
|| (ttyp) == POLY_TRAP \
99-
|| (ttyp) == MAGIC_BEAM_TRAP)
94+
#define is_magical_trap(ttyp) ((ttyp) == TELEP_TRAP_SET \
95+
|| (ttyp) == LEVEL_TELEP \
96+
|| (ttyp) == MAGIC_TRAP_SET \
97+
|| (ttyp) == ANTI_MAGIC \
98+
|| (ttyp) == POLY_TRAP_SET \
99+
|| (ttyp) == MAGIC_BEAM_TRAP_SET)
100100

101101
#endif /* TRAP_H */

src/apply.c

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ STATIC_DCL void FDECL(light_cocktail, (struct obj **));
2424
STATIC_PTR void FDECL(display_jump_positions, (int));
2525
STATIC_DCL void FDECL(use_tinning_kit, (struct obj *));
2626
STATIC_DCL void FDECL(use_grease, (struct obj *));
27+
STATIC_DCL void FDECL(use_trap_kit, (struct obj *));
2728
STATIC_DCL void FDECL(use_trap, (struct obj *));
2829
STATIC_DCL void FDECL(apply_flint, (struct obj **));
2930
STATIC_DCL void FDECL(use_stone, (struct obj **));
@@ -2925,6 +2926,107 @@ reset_trapset()
29252926
trapinfo.force_bungle = 0;
29262927
}
29272928

2929+
static const struct trap_recipe {
2930+
short result_typ;
2931+
short typ;
2932+
short quan;
2933+
} final[] = {
2934+
/* trap type, components, component quantity */
2935+
{ ARROW_TRAP, ARROW, 20 },
2936+
{ 0, 0, 0 }
2937+
};
2938+
2939+
STATIC_OVL void
2940+
use_trap_kit(obj)
2941+
struct obj *obj; /* actual trap kit */
2942+
{
2943+
const struct trap_recipe *recipe;
2944+
struct obj* otmp; /* components needed to make a trap */
2945+
struct obj* output; /* final product (crafted trap) */
2946+
char allowall[2];
2947+
int trap_type = 0;
2948+
2949+
allowall[0] = ALL_CLASSES;
2950+
allowall[1] = '\0';
2951+
2952+
/* various player conditions can prevent successful crafting */
2953+
if (!u_handsy()) {
2954+
return;
2955+
} else if (Stunned || Confusion) {
2956+
You_cant("build a trap while incapacitated.");
2957+
return;
2958+
} else if (u.uhunger < 50) { /* weak */
2959+
You("are too weak from hunger to build a trap.");
2960+
return;
2961+
} else if (ACURR(A_DEX) < 4) {
2962+
You("lack the dexterity to build a trap.");
2963+
return;
2964+
}
2965+
2966+
/* trap kit requires 'charges' to function */
2967+
if (obj->spe <= 0) {
2968+
You("seem to be out of materials to build a trap.");
2969+
return;
2970+
}
2971+
2972+
/* setup the base components for the trap */
2973+
otmp = getobj(allowall, "use as a component");
2974+
if (!otmp) {
2975+
You("need a base component to build a trap.");
2976+
return;
2977+
}
2978+
2979+
/* start the build process */
2980+
for (recipe = final; recipe->result_typ; recipe++) {
2981+
if (otmp->otyp == recipe->typ
2982+
&& otmp->quan >= recipe->quan) {
2983+
trap_type = recipe->result_typ;
2984+
break;
2985+
}
2986+
}
2987+
2988+
if (!trap_type) {
2989+
You("fail to build the trap.");
2990+
return;
2991+
} else if (trap_type) {
2992+
/* success */
2993+
output = mksobj(trap_type, TRUE, FALSE);
2994+
consume_obj_charge(obj, TRUE);
2995+
}
2996+
2997+
/* feedback for successful build */
2998+
pline("Using your %s, you craft %s to build %s.",
2999+
simpleonames(obj), yobjnam(otmp, (char *) 0),
3000+
doname(output));
3001+
3002+
/* ensure the final product is not degraded or coated
3003+
with anything in any way */
3004+
output->cursed = output->blessed = 0;
3005+
output->oeroded = output->oeroded2 = 0;
3006+
output->opoisoned = 0;
3007+
output->otainted = 0;
3008+
output->greased = 0;
3009+
3010+
/* toss out old objects, add new one */
3011+
if (otmp->otyp == recipe->typ)
3012+
otmp->quan -= recipe->quan;
3013+
3014+
/* recalculate weight of the recipe objects if
3015+
using a stack */
3016+
if (otmp->quan > 0)
3017+
otmp->owt = weight(otmp);
3018+
3019+
/* delete recipe objects if quantity reaches zero */
3020+
if (otmp->quan <= 0)
3021+
delobj(otmp);
3022+
3023+
/* trap is created */
3024+
output = addinv(output);
3025+
output->owt = weight(output);
3026+
3027+
update_inventory();
3028+
}
3029+
29283030
/* Place a landmine/bear trap. Helge Hafting */
29293031
STATIC_OVL void
29303032
use_trap(otmp)
@@ -2970,7 +3072,9 @@ struct obj *otmp;
29703072
reset_trapset();
29713073
return;
29723074
}
2973-
ttyp = (otmp->otyp == LAND_MINE) ? LANDMINE : BEAR_TRAP;
3075+
ttyp = (otmp->otyp == LAND_MINE) ? LANDMINE
3076+
: (otmp->otyp == BEARTRAP) ? BEAR_TRAP
3077+
: ARROW_TRAP_SET;
29743078
if (otmp == trapinfo.tobj && u.ux == trapinfo.tx && u.uy == trapinfo.ty) {
29753079
You("resume setting %s%s.", shk_your(buf, otmp),
29763080
defsyms[trap_to_defsym(what_trap(ttyp, rn2))].explanation);
@@ -3007,6 +3111,7 @@ struct obj *otmp;
30073111
trapinfo.time_needed = 0;
30083112
trapinfo.force_bungle = TRUE;
30093113
break;
3114+
case ARROW_TRAP_SET:
30103115
case BEAR_TRAP: /* drop it without arming it */
30113116
reset_trapset();
30123117
You("drop %s!",
@@ -3045,7 +3150,9 @@ set_trap()
30453150
if (--trapinfo.time_needed > 0)
30463151
return 1; /* still busy */
30473152

3048-
ttyp = (otmp->otyp == LAND_MINE) ? LANDMINE : BEAR_TRAP;
3153+
ttyp = (otmp->otyp == LAND_MINE) ? LANDMINE
3154+
: (otmp->otyp == BEARTRAP) ? BEAR_TRAP
3155+
: ARROW_TRAP_SET;
30493156
ttmp = maketrap(u.ux, u.uy, ttyp);
30503157
if (ttmp) {
30513158
ttmp->madeby_u = 1;
@@ -4402,6 +4509,9 @@ doapply()
44024509
case DWARVISH_MATTOCK:
44034510
res = use_pick_axe(obj);
44044511
break;
4512+
case TRAP_KIT:
4513+
use_trap_kit(obj);
4514+
break;
44054515
case TINNING_KIT:
44064516
use_tinning_kit(obj);
44074517
break;
@@ -4519,6 +4629,7 @@ doapply()
45194629
break;
45204630
case LAND_MINE:
45214631
case BEARTRAP:
4632+
case ARROW_TRAP:
45224633
use_trap(obj);
45234634
break;
45244635
case FLINT:

src/dothrow.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -847,8 +847,8 @@ int x, y;
847847
} else if (ttmp->ttyp == VIBRATING_SQUARE) {
848848
pline("The ground vibrates as you pass it.");
849849
dotrap(ttmp, 0); /* doesn't print messages */
850-
} else if (ttmp->ttyp == FIRE_TRAP
851-
|| ttmp->ttyp == ICE_TRAP) {
850+
} else if (ttmp->ttyp == FIRE_TRAP_SET
851+
|| ttmp->ttyp == ICE_TRAP_SET) {
852852
dotrap(ttmp, 0);
853853
} else if ((is_pit(ttmp->ttyp) || is_hole(ttmp->ttyp))
854854
&& Sokoban) {

src/hack.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ moverock()
259259
newsym(rx, ry);
260260
return sobj_at(BOULDER, sx, sy) ? -1 : 0;
261261
case LEVEL_TELEP:
262-
case TELEP_TRAP: {
262+
case TELEP_TRAP_SET: {
263263
int newlev = 0; /* lint suppression */
264264
d_level dest;
265265

@@ -275,7 +275,7 @@ moverock()
275275
else
276276
You("push %s and suddenly it disappears!",
277277
the(xname(otmp)));
278-
if (ttmp->ttyp == TELEP_TRAP) {
278+
if (ttmp->ttyp == TELEP_TRAP_SET) {
279279
(void) rloco(otmp);
280280
} else {
281281
obj_extract_self(otmp);

0 commit comments

Comments
 (0)