Help me make my new variant! (please!)

Collapse
X
 
  • Time
  • Show
Clear All
new posts

  • Pete Mack
    replied
    NULL is the usual. But it is immaterial, since Nick will use a hard assertion.

    Leave a comment:


  • Julian
    replied
    Originally posted by Pete Mack
    @Julian- 0xDDDDDDD is not -1. I suspect it is uninitialized memory left as a buffer by debug allocator.
    Almost certainly — whatever was after body.slots in memory was pointing to uninitialized memory when read as an object.

    -1 (or something clearly wrong that can be tested for) is what slot_by_name() should’ve been returning when the name didn’t map to a slot.

    Leave a comment:


  • Pete Mack
    replied
    @Julian- 0xDDDDDDD is not -1. I suspect it is uninitialized memory left as a buffer by debug allocator.

    Leave a comment:


  • will_asher
    replied
    It looks like most people didn't notice my edit at the bottom of the post:
    Originally posted by will_asher
    EDIT: I think I fixed the problem. Apparently the name of the slot is "arm" not "shield"...

    Leave a comment:


  • Nick
    replied
    Originally posted by Julian
    slot_by_name() should be returning i where it breaks, and returning an error value at the end, probably -1, and slot_object() should be checking that i is within 0<=I<body.count
    I'm actually going to be more hard-core than that - I've put asserts in both places in the latest update.

    Leave a comment:


  • Julian
    replied
    This looks like a bug in slot_by_name:

    Code:
    int slot_by_name(struct player *p, const char *name)
    {
    	int i;
    
    	/* Look for the correctly named slot */
    	for (i = 0; i < p->body.count; i++) {
    		if (streq(name, p->body.slots[i].name)) {
    			break;
    		}
    	}
    
    	/* Index for that slot */
    	return i;
    }
    If the name isn’t valid, you run off the end of the loop, and i is body.count.

    Then this bad index is fed to:
    Code:
    struct object *slot_object(struct player *p, int slot)
    {
    	/* Ensure a valid body */
    	if (p->body.slots && p->body.slots[slot].obj) {
    		return p->body.slots[slot].obj;
    	}
    
    	return NULL;
    }
    The test on body.slots[slot].obj is pointing off the end of the array, and you return… something

    slot_by_name() should be returning i where it breaks, and returning an error value at the end, probably -1, and slot_object() should be checking that i is within 0<=I<body.count

    Leave a comment:


  • Pete Mack
    replied
    @fph--
    This isn't a memory error. It's an ordinary pointer bug that can be discovered by watching values in the debugger. But I don't know the problem by looking at this code fragment. Off the top of my head, it looks like uninitialized memory in debug mode being accessed. I am guessing that 0xDDDDDDDD is MS debugger speak for 0x0BADBEEF in the classical lexicon.
    Last edited by Pete Mack; May 1, 2021, 13:07.

    Leave a comment:


  • fph
    replied
    Unfortunately memory accesses are hard to debug because the true error often lies somewhere else than where the debugger reports. In this case, the true error is where you write an invalid value into `obj`, but the debugger only tells you when you try to use that value many lines later. C is brutal in this respect, because most memory management has to be done manually.

    In this case, I would suggest to add an error check so that the game crashes immediately when you pass equipped_item_by_slot_name a wrong slot name. "Fail fast" is often a good idea.

    Leave a comment:


  • will_asher
    replied
    The problem seems to be something about this part:
    Code:
    	/* The weapon used */
    	struct object *obj = equipped_item_by_slot_name(p, "weapon");
    
    	/* off-weapon attack */
    	if (offhand) obj = equipped_item_by_slot_name(p, "shield");
    
    	/* Information about the attack */
    	int chance = chance_of_melee_hit(p, obj);
    The debugger is saying
    "Exception thrown: read access violation.
    weapon was 0xDDDDDDDD."
    But I don't understand why the assignment to "obj" is working for the main weapon but not for the off-hand weapon in the shield slot.
    The line number it gives me is when it tries to do something with the "obj" (which turns into "weapon") in chance_of_melee_hit() here:
    Code:
    int chance_of_melee_hit(const struct player *p, const struct object *weapon)
    {
    	int chance, bonus = p->state.to_h;
    
    	if (weapon)
    *>*		bonus += weapon->to_h;
    	chance = p->state.skills[SKILL_TO_HIT_MELEE] + bonus * 
    		BTH_PLUS_ADJ;
    	return chance;
    }
    but if it passed the "if (weapon)" part, shouldn't it be able to get the "to_h" from the weapon?

    EDIT: I think I fixed the problem. Apparently the name of the slot is "arm" not "shield"...
    Last edited by will_asher; May 1, 2021, 07:02.

    Leave a comment:


  • will_asher
    replied
    yeah, I'd almost forgotten Visual Studio comes with a debugger. I need to remind myself how to use it...

    Leave a comment:


  • Pete Mack
    replied
    Treat compiler warnings about indirection as hard errors, and you most likely won't go wrong. That said, when a program crashes, we need to know the line number of the crash, and the exact error message. In any case, this is the point at which you should be running RubberBand from the debugger, so you can see the value of variables that caused the crash.

    Leave a comment:


  • Julian
    replied
    Originally posted by will_asher
    In attempt_shield_bash(), after calculating the chance for an off-hand attack (similar to the way a shield bash works), I have this:
    if (offhandhit) {
    if (py_attack_real(p, grid, &fear, true)) return true;
    }
    Here's it's giving me a "differs in levels of indirection" warning on that line. What does that mean?
    Can you see what I did wrong from this?
    We can’t see what you’re doing wrong in the limited context, because we can’t see where the compiler is seeing a pointer to a thing where it’s expecting the thing. (Or vice versa.)

    Having actually looked at the code, it was likely fear, since it’s passed into attempt_shield_bash() as a pointer already, and that’s what py_attack_real() wants, not a pointer to a pointer.

    If I remove the ampersand next to "fear", the warning goes away, but I want to make sure that is the right way to do it.

    EDIT: I compiled after just removing the ampersand, and it's crashing when attacking with the off-hand weapon, so something's not right.
    Yes, but that change was (probably) actually correct, so it’s something else.

    Pointers in C (and strings and arrays — they’re all the same thing) are very easy to get wrong if you don’t know what you’re going, and trying to muddle along, asking questions on the Angband forums whenever something blows up is going to be a very slow and painful process for everyone involved. Find yourself a C tutorial — there are tons of free ones online.

    Leave a comment:


  • backwardsEric
    replied
    Originally posted by will_asher
    In attempt_shield_bash(), after calculating the chance for an off-hand attack (similar to the way a shield bash works), I have this:
    if (offhandhit) {
    if (py_attack_real(p, grid, &fear, true)) return true;
    }
    Here's it's giving me a "differs in levels of indirection" warning on that line. What does that mean?
    Can you see what I did wrong from this?

    If I remove the ampersand next to "fear", the warning goes away, but I want to make sure that is the right way to do it.

    EDIT: I compiled after just removing the ampersand, and it's crashing when attacking with the off-hand weapon, so something's not right.
    Removing the ampersand before "fear" in that call to py_attack_real() from attempt_shield_bash() is right. The ampersand is the "take the address of the argument" operator. The type of its result is a pointer to the type of the argument. Before you corrected that call, the ampersand was generating a bool** (a pointer to a pointer to a bool) while py_attack_real() was declared as expecting a bool* (a pointer to bool). The mismatch between the bool** and bool* was the cause of the error message ("levels of indirection" is the common way to refer to the number of asterisks in a pointer type).

    As for the behavior after fixing the compiler error, you'll need to distinguish between types of crashes. It could be an assertion failure. Those are from checks (via assert() statements in the code itself) that the state of the program is as expected. You may get those as you modify Angband because someone before you may thought something like, "The object the code works with here has to be in the weapon slot. I'll verify that by adding 'assert(object_slot(obj) == EQUIP_WEAPON);'.", and your change means that assumption is no longer valid. For those, you'll have to examine the assertion and the code after it that presumably depends on that assumption. Then either modify that code and the assertion to be compatible with your other change or modify or discard your other change so it will work with the code protected by that assertion.

    Other types of crashes, segmentation faults and illegal instructions, are due to bad logic in the code, frequently involving the use of pointers and sometimes well before the point at which the crash occurred. Most of those crashes may be your fault, but some could be logic problems in the base Angband code (or stuff in the libraries that Angband calls) that had not been recognized because nothing before set up the necessary conditions for the crash to occur.

    Leave a comment:


  • will_asher
    replied
    Working an adding duel-wielding, thinking I'd just add the off-hand attack to the shield bash code.
    I added a parameter to two functions, changing it everywhere the function is used:

    bool attempt_shield_bash(struct player *p, struct loc grid, bool *fear, bool offhandhit)
    bool py_attack_real(struct player *p, struct loc grid, bool *fear, bool offhand)

    In py_attack_real(), I have this:
    /* The weapon used */
    struct object *obj = equipped_item_by_slot_name(p, "weapon");

    /* off-weapon attack */
    if (offhand) obj = equipped_item_by_slot_name(p, "shield");

    In attempt_shield_bash(), after calculating the chance for an off-hand attack (similar to the way a shield bash works), I have this:
    if (offhandhit) {
    if (py_attack_real(p, grid, &fear, true)) return true;
    }
    Here's it's giving me a "differs in levels of indirection" warning on that line. What does that mean?
    Can you see what I did wrong from this?

    If I remove the ampersand next to "fear", the warning goes away, but I want to make sure that is the right way to do it.

    EDIT: I compiled after just removing the ampersand, and it's crashing when attacking with the off-hand weapon, so something's not right.
    Last edited by will_asher; April 30, 2021, 10:23.

    Leave a comment:


  • will_asher
    replied
    Originally posted by Pete Mack
    @Will--
    Potions with temporary bad effects are of very limited value. I usually only use !blind, !pois, !conf and the like exactly once per game--when I ID them. Very occasionally, I use them to ID runes. And that's it. Stat swap potions are more interesting.
    If your stats are:
    STR 18, INT 8, WIS 16, DEX 15, CON 16
    !Mediocrity will damage STR and raise INT. So drink it when you have sustain strength. It's kind of like a stat swap potion.

    Leave a comment:

Working...
😀
😂
🥰
😘
🤢
😎
😞
😡
👍
👎