Angband.oook.cz
Angband.oook.cz
AboutVariantsLadderForumCompetitionComicScreenshotsFunniesLinks

Go Back   Angband Forums > Angband > Development

Reply
 
Thread Tools Display Modes
Old June 25, 2020, 05:15   #1
Celiend
Rookie
 
Join Date: Jun 2020
Posts: 8
Celiend is on a distinguished road
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 at 05:28.
Celiend is offline   Reply With Quote
Old June 25, 2020, 06:45   #2
wobbly
Veteran
 
Join Date: May 2012
Location: Adelaide, Australia
Posts: 2,375
wobbly is on a distinguished road
I'd take a look at how the priest spell "dispel undead" works.

Code:
effect:PROJECT_LOS_AWARE:DISP_UNDEAD
wobbly is offline   Reply With Quote
Old June 25, 2020, 07:09   #3
Celiend
Rookie
 
Join Date: Jun 2020
Posts: 8
Celiend is on a distinguished road
Quote:
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 at 08:08.
Celiend is offline   Reply With Quote
Old June 25, 2020, 07:14   #4
Nick
Vanilla maintainer
 
Nick's Avatar
 
Join Date: Apr 2007
Location: Canberra, Australia
Age: 55
Posts: 8,463
Donated: $60
Nick will become famous soon enough
I think your problem is here:

Quote:
Originally Posted by Celiend View Post
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.
Nick is offline   Reply With Quote
Old June 25, 2020, 07:29   #5
Celiend
Rookie
 
Join Date: Jun 2020
Posts: 8
Celiend is on a distinguished road
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.
Celiend is offline   Reply With Quote
Old June 25, 2020, 07:37   #6
Celiend
Rookie
 
Join Date: Jun 2020
Posts: 8
Celiend is on a distinguished road
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;
}
Celiend is offline   Reply With Quote
Old June 25, 2020, 07:45   #7
Celiend
Rookie
 
Join Date: Jun 2020
Posts: 8
Celiend is on a distinguished road
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;
}
Celiend is offline   Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
modifying monster.txt & monster_base.txt Tibarius Development 3 May 16, 2020 17:13
List of all Class/Race Flags? tynan Vanilla 2 March 29, 2014 05:29
Monster Flags fizzix v4 4 February 9, 2012 23:37
Racial Flags Jungle_Boy Vanilla 1 October 10, 2011 01:17
Too darn many flags. :-( PaulBlay Development 9 April 4, 2009 22:59


All times are GMT +1. The time now is 17:53.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.