Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpellEffect on cubes #3829

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions config/fxdata/cubes.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@
[cube0]
Name = FREE_SPACE
Textures = 0 0 0 0 0 0
; Spell effect to cast on walk-by. Accepts both names and numbers.
SpellEffect = 0
; Defines the level of 'SpellEffect' when cast. A value of 0 uses the target’s level.
SpellLevel = 0
; Defines valid targets for 'SpellEffect'. Options are:
; FRIENDLY - Casts on allies.
; HOSTILE - Casts on enemies.
; NEUTRAL - Casts on neutrals.
; ONLY_DIGGER - Casts only on diggers.
; NOT_DIGGER - Does not cast on diggers.
; ONLY_EVIL - Casts only on evil creatures.
; NOT_EVIL - Does not cast on evil creatures.
; ONLY_FLYING - Casts only on flying creatures.
; NOT_FLYING - Does not cast on flying creatures.
; Example: 'Castability = FRIENDLY NOT_DIGGER NOT_FLYING' casts on allies that are neither flying nor diggers.
Castability = 0
Properties = UNCLAIMED_PATH

; Standard, diggable earth.
Expand Down
93 changes: 90 additions & 3 deletions src/config_cubes.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "game_legacy.h"
#include "config.h"
#include "config_cubes.h"
#include "config_magic.h"
#include "console_cmd.h"
#include "post_inc.h"

#ifdef __cplusplus
Expand All @@ -37,8 +39,11 @@ const struct NamedCommand cubes_cube_commands[] = {
{"Textures", 2},
{"OwnershipGroup", 3},
{"Owner", 4},
{"Properties", 5},
{NULL, 0},
{"SpellEffect", 5},
{"SpellLevel", 6},
{"Castability", 7},
{"Properties", 8},
{NULL, 0},
};

const struct NamedCommand cubes_properties_flags[] = {
Expand All @@ -49,6 +54,19 @@ const struct NamedCommand cubes_properties_flags[] = {
{NULL, 0},
};

const struct NamedCommand cubes_castability_flags[] = {
{"FRIENDLY", CCF_Friendly},
{"HOSTILE", CCF_Hostile},
{"NEUTRAL", CCF_Neutral},
{"ONLY_DIGGER", CCF_OnlyDigger},
{"NOT_DIGGER", CCF_NotDigger},
{"ONLY_EVIL", CCF_OnlyEvil},
{"NOT_EVIL", CCF_NotEvil},
{"ONLY_FLYING", CCF_OnlyFlying},
{"NOT_FLYING", CCF_NotFlying},
{NULL, 0},
};

/******************************************************************************/
struct NamedCommand cube_desc[CUBE_ITEMS_MAX];
/******************************************************************************/
Expand Down Expand Up @@ -76,6 +94,10 @@ TbBool parse_cubes_cube_blocks(char *buf, long len, const char *config_textname,
{
cubest = &game.conf.cube_conf.cube_cfgstats[i];
memset(cubest->code_name, 0, COMMAND_WORD_LEN);
cubest->spell_effect = 0;
cubest->spell_level = 0;
cubest->castability_flags = 0;
cubest->properties_flags = 0;
cube_desc[i].name = cubest->code_name;
cube_desc[i].num = i;
}
Expand Down Expand Up @@ -190,7 +212,72 @@ TbBool parse_cubes_cube_blocks(char *buf, long len, const char *config_textname,
n++;
}
break;
case 5: // Properties
case 5: // SpellEffect
if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0)
{
if (parameter_is_number(word_buf))
{
k = atoi(word_buf);
}
else
{
k = get_id(spell_desc, word_buf);
}
if (k >= 0)
{
cubed->spell_effect = k;
n++;
}
}
if (n < 1)
{
CONFWRNLOG("Couldn't read \"%s\" parameter in [%.*s] block of %s file.",
COMMAND_TEXT(cmd_num), blocknamelen, blockname, config_textname);
}
break;
case 6: // SpellLevel
if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0)
{
k = atoi(word_buf);
if (k >= 0)
{
cubed->spell_level = k;
n++;
}
}
if (n < 1)
{
CONFWRNLOG("Couldn't read \"%s\" parameter in [%.*s] block of %s file.",
COMMAND_TEXT(cmd_num), blocknamelen, blockname, config_textname);
}
break;
case 7: // Castability
cubed->castability_flags = 0;
while (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0)
{
if (parameter_is_number(word_buf))
{
k = atoi(word_buf);
cubed->castability_flags = k;
n++;
}
else
{
k = get_id(cubes_castability_flags, word_buf);
if (k > 0)
{
set_flag(cubed->castability_flags, k);
n++;
}
}
}
if (n < 1)
{
CONFWRNLOG("Couldn't read \"%s\" parameter in [%.*s] block of %s file.",
COMMAND_TEXT(cmd_num), blocknamelen, blockname, config_textname);
}
break;
case 8: // Properties
cubed->properties_flags = 0;
while (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0)
{
Expand Down
16 changes: 16 additions & 0 deletions src/config_cubes.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,27 @@ enum CubePropertiesFlags {
CPF_IsUnclaimedPath = 0x08,
};

enum CubeCastabilityFlags {
CCF_None = 0x000,
CCF_Friendly = 0x001,
CCF_Hostile = 0x002,
CCF_Neutral = 0x004,
CCF_OnlyDigger = 0x008,
CCF_NotDigger = 0x010,
CCF_OnlyEvil = 0x020,
CCF_NotEvil = 0x040,
CCF_OnlyFlying = 0x080,
CCF_NotFlying = 0x100,
};

struct CubeConfigStats {
char code_name[COMMAND_WORD_LEN];
unsigned short texture_id[CUBE_TEXTURES];
unsigned char ownershipGroup;
PlayerNumber owner;
SpellKind spell_effect;
CrtrExpLevel spell_level;
unsigned short castability_flags;
unsigned char properties_flags;
};

Expand Down
149 changes: 138 additions & 11 deletions src/thing_creature.c
Original file line number Diff line number Diff line change
Expand Up @@ -6146,34 +6146,161 @@ void process_magic_fall_effect(struct Thing *thing)
}
}

TbBool cube_castability_can_target_creature(struct Thing *thing, PlayerNumber plyr_idx, unsigned short castability_flags)
{
// Exclude 'castability_flags == 0' or spectators immediately.
if ((castability_flags == 0)
|| flag_is_set(get_creature_model_flags(thing), CMF_IsSpectator))
{
return false;
}
// Handle digger-related flags.
if (flag_is_set(get_creature_model_flags(thing), CMF_IsSpecDigger))
{
if (flag_is_set(castability_flags, CCF_NotDigger))
{
return false;
}
}
else
{
if (flag_is_set(castability_flags, CCF_OnlyDigger))
{
return false;
}
}
// Handle evil-related flags.
if (flag_is_set(get_creature_model_flags(thing), CMF_IsEvil))
{
if (flag_is_set(castability_flags, CCF_NotEvil))
{
return false;
}
}
else
{
if (flag_is_set(castability_flags, CCF_OnlyEvil))
{
return false;
}
}
// Handle flying-related flags. A creature is considered flying if its z-position is above floor height at x/y coordinates. Relevant for possession.
if (thing->mappos.z.val > subtile_coord(get_navigation_map_floor_height(thing->mappos.x.stl.num, thing->mappos.y.stl.num), 0))
{
if (flag_is_set(castability_flags, CCF_NotFlying))
{
return false;
}
}
else
{
if (flag_is_set(castability_flags, CCF_OnlyFlying))
{
return false;
}
}
// Handle owner-related flags.
if (!is_neutral_thing(thing))
{
if (players_are_mutual_allies(thing->owner, plyr_idx))
{
if (flag_is_set(castability_flags, CCF_Friendly))
{
return true;
}
}
else
{
if (flag_is_set(castability_flags, CCF_Hostile))
{
return true;
}
}
}
else
{
if (flag_is_set(castability_flags, CCF_Neutral))
{
return true;
}
}
// Exclude target by default if no flags match.
return false;
}

void process_cube_spell_effect_on_thing(struct Thing *thing, int cube_kind)
{
// Cubes can apply spell effect if set.
struct CubeConfigStats* cubest = get_cube_model_stats(cube_kind);
if (cubest->spell_effect > 0)
{
// Check if already affected.
struct CreatureControl* cctrl = creature_control_get_from_thing(thing);
TbBool affected = false;
for (int k = 0; k < CREATURE_MAX_SPELLS_CASTED_AT; k++)
{
if (cctrl->casted_spells[k].spkind == cubest->spell_effect)
{
affected = true;
break;
}
}
// Do not cast if already affected.
if (!affected)
{
PlayerNumber plyr_idx = get_slab_owner_thing_is_on(thing);
if (cube_castability_can_target_creature(thing, plyr_idx, cubest->castability_flags))
{
struct SpellConfig* spconf = get_spell_config(cubest->spell_effect);
// Even though immunity is handled in 'apply_spell_effect_to_thing', check it here to prevent sound effects.
if (!creature_is_immune_to_spell_effect(thing, spconf->spell_flags))
{
if (spconf->caster_affect_sound > 0)
{
thing_play_sample(thing, spconf->caster_affect_sound + UNSYNC_RANDOM(spconf->caster_sounds_count), NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS);
}
if (cubest->spell_level > 0)
{
apply_spell_effect_to_thing(thing, cubest->spell_effect, cubest->spell_level-1, plyr_idx);
}
else
{
apply_spell_effect_to_thing(thing, cubest->spell_effect, cctrl->exp_level, plyr_idx);
}
}
}
}
}
}

void process_landscape_affecting_creature(struct Thing *thing)
{
SYNCDBG(18,"Starting");
thing->movement_flags &= ~TMvF_IsOnWater;
thing->movement_flags &= ~TMvF_IsOnLava;
thing->movement_flags &= ~TMvF_IsOnSnow;
clear_flag(thing->movement_flags, TMvF_IsOnWater);
clear_flag(thing->movement_flags, TMvF_IsOnLava);
clear_flag(thing->movement_flags, TMvF_IsOnSnow);
struct CreatureControl* cctrl = creature_control_get_from_thing(thing);
if (creature_control_invalid(cctrl))
{
ERRORLOG("Invalid creature control; no action");
return;
}
cctrl->corpse_to_piss_on = 0;

int stl_idx = get_subtile_number(thing->mappos.x.stl.num, thing->mappos.y.stl.num);
int cube_kind = get_top_cube_at_pos(stl_idx);
process_cube_spell_effect_on_thing(thing, cube_kind);
unsigned long navheight = get_navigation_map_floor_height(thing->mappos.x.stl.num, thing->mappos.y.stl.num);
if (subtile_coord(navheight,0) == thing->mappos.z.val)
if (subtile_coord(navheight, 0) == thing->mappos.z.val)
{
int i = get_top_cube_at_pos(stl_idx);
if (cube_is_lava(i))
if (cube_is_lava(cube_kind))
{
struct CreatureStats* crstat = creature_stats_get_from_thing(thing);
apply_damage_to_thing_and_display_health(thing, crstat->hurt_by_lava, -1);
thing->movement_flags |= TMvF_IsOnLava;
} else
if (cube_is_water(i))
set_flag(thing->movement_flags, TMvF_IsOnLava);
}
else if (cube_is_water(cube_kind))
{
thing->movement_flags |= TMvF_IsOnWater;
set_flag(thing->movement_flags, TMvF_IsOnWater);
}
process_creature_leave_footsteps(thing);
process_creature_standing_on_corpses_at(thing, &thing->mappos);
Expand Down