Monster_Base Flags

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Celiend
    Rookie
    • Jun 2020
    • 8

    Monster_Base Flags

    Hey there!

    I have been messing around with some of the code on the "4.2-Release" branch of Angband Vanilla. Unfortunately, I'm finding that this codebase is *SO* large that I'm having trouble figuring out how to grab simple parameters for alteration.

    So... what I'm trying to do is to figure out how to alter the function "effect_handler_TAP_UNLIFE" in the file "effects.c".

    Origin_Behavior: Current behavior in "4.2-Release" is described as: "When 'Tap Unlife' is cast by the necromancer; it selects the closest undead monster in Line of Sight, and it damages the undead monster as it restores some spell points to the necromancer based on the damage to the undead monster."

    Current behavior:
    Code:
    bool effect_handler_TAP_UNLIFE(effect_handler_context_t *context)
    {
    	int amount = effect_calculate_value(context, false);
    	struct loc target;
    	struct monster *mon = NULL;
    	char m_name[80];
    	int drain = 0;
    	bool fear = false;
    	bool dead = false;
    
    	context->ident = true;
    
    	/* Closest living monster */
    	if (!target_set_closest(TARGET_KILL, monster_is_undead)) {
    		return false;
    	}
    	target_get(&target);
    	mon = target_get_monster();
    
    	/* Hurt the monster */
    	monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
    	msg("You draw power from the %s.", m_name);
    	drain = MIN(mon->hp, amount) / 4;
    	dead = mon_take_hit(mon, amount, &fear, " is destroyed!");
    
    	/* Gain mana */
    	effect_simple(EF_RESTORE_MANA, context->origin, format("%d", drain), 0, 0,
    				  0, 0, 0, NULL);
    
    	/* Handle fear for surviving monsters */
    	if (!dead && monster_is_visible(mon)) {
    		message_pain(mon, amount);
    		if (fear) {
    			add_monster_message(mon, MON_MSG_FLEE_IN_TERROR, true);
    		}
    	}
    
    	return true;
    }

    Changed_Behavior: Intended change to the behavior to the "Tap Unlife" spell in a private branch called "Necromancer_Changes" is: "When 'Tap Unlife' is cast by the necromancer; it selects all undead monsters in Line of Sight and it damages the undead monsters as it restores some spell points to the necromancer based on the damage inflicted to one instance of an undead monster."

    My code changes:

    Code:
    bool effect_handler_TAP_UNLIFE(effect_handler_context_t *context)
    {
    	int amount = effect_calculate_value(context, false);
    	struct loc target;
    	struct monster *mon = NULL;
    	char m_name[80];
    	int drain = 0;
    	bool fear = false;
    	bool dead = false;
            
            msg("XXXCalled Tap Unlife:  amount: %i.",amount);
    
    	context->ident = true;
    
    	/* Closest living monster */
    	if (!target_set_closest(TARGET_KILL, monster_is_undead)) {
    		return false;
    	}
    	target_get(&target);
    	mon = target_get_monster();
            
            struct loc origin = origin_get_loc(context->origin);
            
            int i = 0;
            
            for (i = 1; i < cave_monster_max(cave); i++) {
                struct monster *mon = cave_monster(cave, i);
    
            	/* Paranoia -- Skip dead monsters */
                if (!mon->race) continue;
    
    	    /* Require line of sight */
                if (!los(cave, origin, mon->grid)) continue;
    
    	    /* Jump directly to the monster */
                	/* Hurt the monster */
                if(monster_is_undead)
                {
                    monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
                    msg("You draw power from the %s.", m_name);
                    drain = MIN(mon->hp, amount) / 4;
                    dead = mon_take_hit(mon, amount, &fear, " is destroyed!");        
                }
    	    
                context->ident = true;
    	}
    
    	/* Hurt the monster */
            /*
            monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
    	msg("You draw power from the %s.", m_name);
    	drain = MIN(mon->hp, amount) / 4;
    	dead = mon_take_hit(mon, amount, &fear, " is destroyed!"); 
             */
    	
    	/* Gain mana */
    	effect_simple(EF_RESTORE_MANA, context->origin, format("%d", drain), 0, 0,
    				  0, 0, 0, NULL);
    
    	/* Handle fear for surviving monsters */
            /*
            if (!dead && monster_is_visible(mon)) {
    		message_pain(mon, amount);
    		if (fear) {
    			add_monster_message(mon, MON_MSG_FLEE_IN_TERROR, true);
    		}
    	} 
             */
    	
    
    	return true;
    }

    You'll notice if you test this code that every monster in Line of Sight is currently targeted for damage (Undead or not) [Apparently, 'monster_is_undead' currently always evaluates to 'true'] as long as at least one Undead is in Line of Sight.

    This behavior is undesirable... I would like to target undead exclusively with the casting of "Tap Unlife". I've been trying to figure out how to grab a property for a given monster that will tell me if the monster has a flag, 'UNDEAD'... but I'm coming up dry.

    For those of you with more experience editing the vanilla Angband codebase... if you know how to resolve this problem... would you mind suggesting a solution?

    Note... I have NO INTENTION of replacing the current vanilla behavior with my changes as part of "canon"; I'm just... trying stuff right now.
    Last edited by Celiend; June 25, 2020, 05:28.
  • wobbly
    Prophet
    • May 2012
    • 2631

    #2
    I'd take a look at how the priest spell "dispel undead" works.

    Code:
    effect:PROJECT_LOS_AWARE:DISP_UNDEAD

    Comment

    • Celiend
      Rookie
      • Jun 2020
      • 8

      #3
      I'd take a look at how the priest spell "dispel undead" works.
      Perfect. It makes sense that what I'm trying to do is, in fact, exactly what "dispel undead" does.

      Code:
      spell:Dispel Undead:10:14:55:6
      effect:PROJECT_LOS_AWARE:DISP_UNDEAD
      dice:d$S
      expr:S:PLAYER_LEVEL:* 3
      desc:Inflicts unresistable damage on each undead monster within line of sight.
      Code:
      bool effect_handler_PROJECT_LOS_AWARE(effect_handler_context_t *context)
      {
      	int i;
      	int dam = effect_calculate_value(context, context->other ? true : false);
      	int typ = context->subtype;
      
      	int flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
      
      	if (context->aware) flg |= PROJECT_AWARE;
      
      	/* Affect all (nearby) monsters */
      	for (i = 1; i < cave_monster_max(cave); i++) {
      		struct monster *mon = cave_monster(cave, i);
      		struct loc grid;
      
      		/* Paranoia -- Skip dead monsters */
      		if (!mon->race) continue;
      
      		/* Location */
      		grid = mon->grid;
      
      		/* Require line of sight */
      		if (!square_isview(cave, grid)) continue;
      
      		/* Jump directly to the target monster */
      		(void)project(source_player(), 0, grid, dam, typ, flg, 0, 0, context->obj);
      		context->ident = true;
      	}
      
      	/* Result */
      	return true;
      }
      There appears to be some "black magic" with respect to how the "context" gets defined; "int typ = context->subtype;" must be interpreted from what the parser reads?
      Last edited by Celiend; June 25, 2020, 08:08.

      Comment

      • Nick
        Vanilla maintainer
        • Apr 2007
        • 9637

        #4
        I think your problem is here:

        Originally posted by Celiend
        Code:
                    	/* Hurt the monster */
                    if(monster_is_undead)
                    {
        You need
        Code:
        if(monster_is_undead(mon))
        Currently the if statement always evaluates true because the statement in parentheses is non-zero (it has to be, because the function exists); you want the return value of the function called with "mon" as the argument.
        One for the Dark Lord on his dark throne
        In the Land of Mordor where the Shadows lie.

        Comment

        • Celiend
          Rookie
          • Jun 2020
          • 8

          #5
          Problem solved!!! Thank you wobbly!

          The code is sinfully ugly; but mechanically, it's doing exactly what I want it to! (To be clear... the sinfully ugly part is "MY FAULT" not due to your suggestion)

          Using wobbly's suggested approach:

          Code:
          /**
           * Draw energy from a nearby undead
           */
          bool effect_handler_TAP_UNLIFE(effect_handler_context_t *context)
          {
          	int amount = effect_calculate_value(context, false);
          	struct loc target;
          	struct monster *mon = NULL;
          	char m_name[80];
                  
                  // Hardcoded to affect Undead?
                  int typ = 40;
                  
                  int flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
          
          	if (context->aware) flg |= PROJECT_AWARE;
                  
          	int drain = 0;
          	bool fear = false;
          	bool dead = false;
                  
                  msg("XXXCalled Tap Unlife:  amount: %i.",amount);
          
          	context->ident = true;
          
          	/* Closest living monster */
          	if (!target_set_closest(TARGET_KILL, monster_is_undead)) {
          		return false;
          	}
          	target_get(&target);
          	mon = target_get_monster();
                  
                  struct loc origin = origin_get_loc(context->origin);
                  
                  int i = 0;
                  
                  for (i = 1; i < cave_monster_max(cave); i++) {
                      
                      struct monster *mon = cave_monster(cave, i);
          
                      struct loc grid;
          
                      /* Location */
                      grid = mon->grid;
                      
                  	/* Paranoia -- Skip dead monsters */
                      if (!mon->race) continue;
          
          	    /* Require line of sight */
                      if (!los(cave, origin, mon->grid)) continue;
          
          	    /* Jump directly to the monster */
                      	/* Hurt the monster */
                      
                      /*
                      if(monster_is_undead)
                      {
                          monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
                          msg("You draw power from the %s.", m_name);
                          drain = MIN(mon->hp, amount) / 4;
                          dead = mon_take_hit(mon, amount, &fear, " is destroyed!");        
                      } 
                       */
                      
                      (void)project(source_player(), 0, grid, amount, typ, flg, 0, 0, context->obj);
          	    
                      context->ident = true;
          	}
          I will be trying Nick's suggested approach next to see if it works... (somehow, it didn't occur to me that 'monster_is_undead' is a function)

          Thank you to the both of you for responding.

          Comment

          • Celiend
            Rookie
            • Jun 2020
            • 8

            #6
            Nick's suggested solution works as intended, as well!

            This is neat!!!

            Thank you again for responding, wobbly and Nick!

            This code is too ugly for me to attempt to submit it as part of a 'pull request'; and mechanically, the playerbase would probably object to the affect this behavior would have on the balance of the Necromancer... so it may be odious from those grounds.

            I've exerted some direct control over the Vanilla Angband code-base! I am practically squeeking with joy, atm!

            Code:
            bool effect_handler_TAP_UNLIFE(effect_handler_context_t *context)
            {
            	int amount = effect_calculate_value(context, false);
            	struct loc target;
            	struct monster *mon = NULL;
            	char m_name[80];
                    
                    // Hardcoded to affect Undead?
                    int typ = 40;
                    
                    int flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
            
            	if (context->aware) flg |= PROJECT_AWARE;
                    
            	int drain = 0;
            	bool fear = false;
            	bool dead = false;
                    
                    msg("XXXCalled Tap Unlife:  amount: %i.",amount);
            
            	context->ident = true;
            
            	/* Closest living monster */
            	if (!target_set_closest(TARGET_KILL, monster_is_undead)) {
            		return false;
            	}
            	target_get(&target);
            	mon = target_get_monster();
                    
                    struct loc origin = origin_get_loc(context->origin);
                    
                    int i = 0;
                    
                    for (i = 1; i < cave_monster_max(cave); i++) {
                        
                        struct monster *mon = cave_monster(cave, i);
            
                        struct loc grid;
            
                        /* Location */
                        grid = mon->grid;
                        
                    	/* Paranoia -- Skip dead monsters */
                        if (!mon->race) continue;
            
            	    /* Require line of sight */
                        if (!los(cave, origin, mon->grid)) continue;
            
            	    /* Jump directly to the monster */
                        	/* Hurt the monster */
                        
                        
                        if(monster_is_undead(mon))
                        {
                            monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
                            msg("You draw power from the %s.", m_name);
                            drain = MIN(mon->hp, amount) / 4;
                            dead = mon_take_hit(mon, amount, &fear, " is destroyed!");        
                        } 
                        
                        //(void)project(source_player(), 0, grid, amount, typ, flg, 0, 0, context->obj);
            	    
                        context->ident = true;
            	}
            
            	/* Hurt the monster */
                    /*
                    monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
            	msg("You draw power from the %s.", m_name);
            	drain = MIN(mon->hp, amount) / 4;
            	dead = mon_take_hit(mon, amount, &fear, " is destroyed!"); 
                     */
            	
            	/* Gain mana */
            	effect_simple(EF_RESTORE_MANA, context->origin, format("%d", drain), 0, 0,
            				  0, 0, 0, NULL);
            
            	/* Handle fear for surviving monsters */
                    /*
                    if (!dead && monster_is_visible(mon)) {
            		message_pain(mon, amount);
            		if (fear) {
            			add_monster_message(mon, MON_MSG_FLEE_IN_TERROR, true);
            		}
            	} 
                     */
            	
            
            	return true;
            }

            Comment

            • Celiend
              Rookie
              • Jun 2020
              • 8

              #7
              Here is the pretty version:

              Code:
              /**
               * Draw energy from all nearby undead
               */
              bool effect_handler_TAP_UNLIFE(effect_handler_context_t *context)
              {
              	int amount = effect_calculate_value(context, false);
              	struct loc target;
              	struct monster *mon = NULL;
              	char m_name[80];
                     
              	int drain = 0;
              	bool fear = false;
              	bool dead = false;
                      
              	context->ident = true;
              
              	/* Closest living monster */
              	if (!target_set_closest(TARGET_KILL, monster_is_undead)) {
              		return false;
              	}
              	target_get(&target);
              	mon = target_get_monster();
                      
                      struct loc origin = origin_get_loc(context->origin);
                      
                      int i = 0;
                      
                      for (i = 1; i < cave_monster_max(cave); i++) {
                          
                          struct monster *mon = cave_monster(cave, i);
              
                          struct loc grid;
              
                          /* Location */
                          grid = mon->grid;
                          
                      	/* Paranoia -- Skip dead monsters */
                          if (!mon->race) continue;
              
              	    /* Require line of sight */
                          if (!los(cave, origin, mon->grid)) continue;
              
              	    /* Jump directly to the monster */
                          /* Hurt the monster */            
                          if(monster_is_undead(mon))
                          {
                              monster_desc(m_name, sizeof(m_name), mon, MDESC_TARG);
                              msg("You draw power from the %s.", m_name);
                              drain = MIN(mon->hp, amount) / 4;
                              dead = mon_take_hit(mon, amount, &fear, " is destroyed!");        
                          } 
                          	    
                          context->ident = true;
              	}
              	
              	/* Gain mana */
              	effect_simple(EF_RESTORE_MANA, context->origin, format("%d", drain), 0, 0,
              				  0, 0, 0, NULL);	
              
              	return true;
              }

              Comment

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