Stealth is exponential--an increase of +3 cuts the chance of waking (per game turn) by a factor of two. At stealth 30, the chance of detection is cut by 1/1000 (compares to a H-T warrior). Monsters can still wake up, but it is highly unlikely in the time it takes to cross a room, especially at speed +10 or higher.
Help me make my new variant! (please!)
Collapse
X
-
It looks like this is actually a bit of work. The way I would do it is:- Add a function (probably in obj-util.c) called something like object_weight(). This should take a struct object * argument, and should take the object's actual weight and then run through all the curses on the object add weight whenever a curse has weight, and return the total.
- Then search though for all uses of object weight in calculations or display, and replace obj->weight with object_weight(obj).
Now vanilla can copy me.
Code:/* Modifiers to object weight */ int object_weight(const struct object* obj) { int weight = obj->weight; int index = 0; /* Check for curses */ struct curse_data* curse = obj ? obj->curses : NULL; /* (copied from calc_bonuses()) */ while (obj) { /* Curses may affect weight (index zero is original object) */ if ((obj->weight) && (index)) weight += obj->weight; /* next curse if any */ if (curse) { index++; obj = NULL; while (index < z_info->curse_max) { if (curse[index].power) { obj = curses[index].obj; break; } else { index++; } } } else { obj = NULL; } } return weight; }
Will_Asher
aka LibraryAdventurer
My old variant DaJAngband:
http://sites.google.com/site/dajangbandwebsite/home (defunct and so old it's forked from Angband 3.1.0 -I think- but it's probably playable...)Comment
-
Here's the usual idiom.
Check preconditions. If not met, return early
Think what your iterator is. If it is over an array, usually use a for loop. Figure your termination condition.
You will see this idiom all through the code base. It s standard for a reason. (In this case it's a relational aggregator.)
Code:/* Modifiers to object weight */ int object_weight(const struct object* obj) { int weight = obj->weight; const struct curse* curses = obj-> curses; if (!curses) return weight; for (int i=0; i < z_info->curse_max && curses[i].power; i++) { if ( [[[ curses[i] type is cumbersome ]]]) weight += [[[ cumbersome curse constant ]]]; } return weight; }
Last edited by Pete Mack; August 29, 2021, 09:59.Comment
-
Pete, I think you're missing that there is a struct object associated with each curse for easy storage of object properties, so the loop need to run across all the curse objects and add their weight.
So I think the original code is correct.One for the Dark Lord on his dark throne
In the Land of Mordor where the Shadows lie.Comment
-
The reason it's like this is that when I was designing the curse struct, I found myself mimicking the object struct, which makes sense because curses just change the behaviour of an object.One for the Dark Lord on his dark throne
In the Land of Mordor where the Shadows lie.Comment
-
Doing grenades was easy was DaJAngband, but now the way the code has changed, a lot of things are harder to customize for someone like me who's not fluent in C.
Here's what I'm trying to do:
(in player_attack.c)
Code:*/ static struct attack_result make_ranged_throw(struct player *p, struct object *obj, struct loc grid) { char *hit_verb = mem_alloc(20 * sizeof(char)); struct attack_result result = {false, 0, 0, hit_verb}; struct monster *mon = square_monster(cave, grid); int b = 0, s = 0; my_strcpy(hit_verb, "hits", 20); /* If we missed then we're done */ if (!test_hit(chance_of_missile_hit(p, obj, NULL, mon), mon->race->ac)) return result; result.success = true; /* EXPLODEing (with grenades) works separately */ if (of_has(obj->flags, OF_EXPLODE)) { result.dmg = ranged_damage(p, mon, obj, NULL, 0, 0); /* Use "STRIKE" effect but use "other" param to bypass asking for target and requirement of being in view of the PC. */ effect_simple(EF_STRIKE, source_player(), format("%d", result.dmg), 0, 2, 9, grid->y, grid->x, NULL); return result; } ...<rest of function>
Also, the way the code does brands and slays now, I don't know how to get the brand type from the grenade to know what subtype to use with effect_simple here.Will_Asher
aka LibraryAdventurer
My old variant DaJAngband:
http://sites.google.com/site/dajangbandwebsite/home (defunct and so old it's forked from Angband 3.1.0 -I think- but it's probably playable...)Comment
-
A couple of notes about the code: (I've marked inline with an ascii arrow.)
Code:/* Modifiers to object weight */ int object_weight(const struct object* obj) { <--- probably a good idea to assert that obj != NULL here. int weight = obj->weight; <-- This assumes obj is not null, or Undefined Behavior ensues int index = 0; /* Check for curses */ struct curse_data* curse = obj ? obj->curses : NULL; ^-- You don't need to test for "obj" here, it cannot be null by definition because you've dereferenced it when initializing "weight" above /* (copied from calc_bonuses()) */ while (obj) { /* Curses may affect weight (index zero is original object) */ if ((obj->weight) && (index)) weight += obj->weight; ^-- You don't need to have "obj->weight" in the condition here, adding 0 doesn't change anything, so you don't need to check EDIT: You can also just declare "index = 1" rather than "index = 0" at the start and drop the conditional altogether If index starts at 1 you can just add the weight regardless. /* next curse if any */ if (curse) { index++; obj = NULL; while (index < z_info->curse_max) { if (curse[index].power) { obj = curses[index].obj; break; } else { ^-- No need for else, just use "index++" without an else around it. If the previous condition is true, you'll break out of the loop anyway. index++; } } } else { obj = NULL; } } return weight; }
Last edited by AnonymousHero; September 3, 2021, 16:57.Comment
-
A couple of notes about the code: (I've marked inline with an ascii arrow.)
Code:while (obj) { /* Curses may affect weight (index zero is original object) */ if ((obj->weight) && (index)) weight += obj->weight; ^-- You don't need to have "obj->weight" in the condition here, adding 0 doesn't change anything, so you don't need to check EDIT: You can also just declare "index = 1" rather than "index = 0" at the start and drop the conditional altogether If index starts at 1 you can just add the weight regardless. /* next curse if any */ if (curse) { index++; obj = NULL; while (index < z_info->curse_max) { if (curse[index].power) { obj = curses[index].obj; break; } else { ^-- No need for else, just use "index++" without an else around it. If the previous condition is true, you'll break out of the loop anyway. index++; } } } else { obj = NULL; } }
Comment
-
The EXPLODE flag just multiplies raw damage by 3. I think here you get to decide what subtype you want to use - the obvious possibilities are MISSILE for just raw damage, or if you want it to feel more like a physical explosion FIRE or SHARDS (or both one after the other).One for the Dark Lord on his dark throne
In the Land of Mordor where the Shadows lie.Comment
-
(EDIT: I don't want the STRIKE effect damage to depend on damage to the particular monster it hits because other nearby monsters may have different resistances.)Will_Asher
aka LibraryAdventurer
My old variant DaJAngband:
http://sites.google.com/site/dajangbandwebsite/home (defunct and so old it's forked from Angband 3.1.0 -I think- but it's probably playable...)Comment
-
Ordinarily, it'll be fire and shards, but grenades can get brand egos. I just don't know how to make the brand egos work with them.
(EDIT: I don't want the STRIKE effect damage to depend on damage to the particular monster it hits because other nearby monsters may have different resistances.)Comment
-
Can they have more than one brand? If only one is possible, then you could look through the brand array to see what ,if anything, is set, look up the related modifier and element for the brand, and use those when you invoke the effect to handle the grenade's explosion.
So this is what I have so far:
in make_ranged_throw()
Code:/* EXPLODEing (with grenades) works separately (half FIRE and half SHARD) (but they have a chance to dud and not explode) */ if ((of_has(obj->flags, OF_EXPLODE)) && (randint0(100) < 96 + player->p_luck)) { int elem = ELEM_FIRE; result.dmg = ranged_damage(p, mon, obj, NULL, 0, 0); /* (doubled) */ if (obj->brands) { elem = grenade_brand(obj); /* It's already being doubled, so this makes it x3 */ result.dmg = (result.dmg * 3 + 1) / 2; } /* Use "STRIKE" effect with "other" param to bypass asking for target and requirement of being in view of the PC. */ effect_simple(EF_STRIKE, source_player(), format("%d", result.dmg), ELEM_SHARD, 2, 9, grid.y, grid.x, NULL); effect_simple(EF_STRIKE, source_player(), format("%d", result.dmg), elem, 2, 9, grid.y, grid.x, NULL); (EDIT: fixed) return result; }
Code:<EDIT: see below. snipped incomplete version>
EDIT: okay, here's what I did:
Code:/* Get a brand from a grenade */ int grenade_brand(struct object* obj) { int i; /* Brands (copied from improve_attack_modifier()) */ for (i = 1; i < z_info->brand_max; i++) { struct brand* b = &brands[i]; if (!obj->brands[i]) continue; if (b->resist_flag == RF_IM_ACID) return ELEM_ACID; if (b->resist_flag == RF_IM_COLD) return ELEM_COLD; if (b->resist_flag == RF_IM_FIRE) return ELEM_FIRE; if (b->resist_flag == RF_IM_ELEC) return ELEM_ELEC; if (b->resist_flag == RF_IM_POIS) return ELEM_POIS; } /* shouldn't get here because this is only called if there's a brand on the grenade */ return ELEM_FIRE; }
Last edited by will_asher; September 6, 2021, 23:32.Will_Asher
aka LibraryAdventurer
My old variant DaJAngband:
http://sites.google.com/site/dajangbandwebsite/home (defunct and so old it's forked from Angband 3.1.0 -I think- but it's probably playable...)Comment
Comment