11
This commit is contained in:
281
Source/ArachnaeSwarm/Verbs/Verb_ShootSelfUnderfoot.cs
Normal file
281
Source/ArachnaeSwarm/Verbs/Verb_ShootSelfUnderfoot.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class Verb_ShootSelfUnderfoot : Verb_LaunchProjectile
|
||||
{
|
||||
// 重写ShotsPerBurst,与Verb_Shoot相同
|
||||
protected override int ShotsPerBurst => base.BurstShotCount;
|
||||
|
||||
// 重写WarmupComplete,添加射击技能学习
|
||||
public override void WarmupComplete()
|
||||
{
|
||||
base.WarmupComplete();
|
||||
|
||||
// 只有在目标是Pawn时才学习射击技能
|
||||
if (currentTarget.Thing is Pawn targetPawn &&
|
||||
!targetPawn.Downed &&
|
||||
!targetPawn.IsColonyMech &&
|
||||
CasterIsPawn &&
|
||||
CasterPawn.skills != null)
|
||||
{
|
||||
float xp = targetPawn.HostileTo(caster) ? 170f : 20f;
|
||||
float num2 = verbProps.AdjustedFullCycleTime(this, CasterPawn);
|
||||
CasterPawn.skills.Learn(SkillDefOf.Shooting, xp * num2);
|
||||
}
|
||||
}
|
||||
|
||||
// 核心重写:将目标改为脚下
|
||||
protected override bool TryCastShot()
|
||||
{
|
||||
// 保存原始目标
|
||||
LocalTargetInfo originalTarget = currentTarget;
|
||||
|
||||
try
|
||||
{
|
||||
// 将目标改为施法者自己的位置
|
||||
currentTarget = new LocalTargetInfo(caster.Position);
|
||||
|
||||
// 调用基类方法,但使用修改后的目标(脚下)
|
||||
bool result = base.TryCastShot();
|
||||
|
||||
// 如果成功发射,记录射击次数
|
||||
if (result && CasterIsPawn)
|
||||
{
|
||||
CasterPawn.records.Increment(RecordDefOf.ShotsFired);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 恢复原始目标(对于连续射击可能重要)
|
||||
currentTarget = originalTarget;
|
||||
}
|
||||
}
|
||||
|
||||
// 重写CanHitTarget,因为目标是脚下,总是可以命中
|
||||
public override bool CanHitTarget(LocalTargetInfo targ)
|
||||
{
|
||||
// 对于向脚下射击,我们总是允许(只要施法者存在)
|
||||
if (caster == null || !caster.Spawned)
|
||||
return false;
|
||||
|
||||
// 如果目标就是施法者自己,允许
|
||||
if (targ == caster)
|
||||
return true;
|
||||
|
||||
// 对于其他目标,使用默认逻辑
|
||||
return base.CanHitTarget(targ);
|
||||
}
|
||||
|
||||
// 重写CanHitTargetFrom,对于脚下射击总是返回true
|
||||
public override bool CanHitTargetFrom(IntVec3 root, LocalTargetInfo targ)
|
||||
{
|
||||
// 如果目标是施法者自己(或脚下),总是可以命中
|
||||
if (targ.Thing == caster || (targ.IsValid && targ.Cell == caster.Position))
|
||||
return true;
|
||||
|
||||
return base.CanHitTargetFrom(root, targ);
|
||||
}
|
||||
|
||||
// 重写TryFindShootLineFromTo,对于脚下射击简化逻辑
|
||||
public new bool TryFindShootLineFromTo(IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine, bool ignoreRange = false)
|
||||
{
|
||||
// 如果目标是脚下,直接返回射击线
|
||||
if (targ.IsValid && targ.Cell == caster.Position)
|
||||
{
|
||||
resultingLine = new ShootLine(root, targ.Cell);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 否则使用基类逻辑
|
||||
return base.TryFindShootLineFromTo(root, targ, out resultingLine, ignoreRange);
|
||||
}
|
||||
|
||||
// 重写DrawHighlight,简化高亮显示
|
||||
public override void DrawHighlight(LocalTargetInfo target)
|
||||
{
|
||||
// 绘制标准射程环
|
||||
verbProps.DrawRadiusRing(caster.Position, this);
|
||||
|
||||
// 如果目标是有效的,绘制目标高亮
|
||||
if (target.IsValid)
|
||||
{
|
||||
GenDraw.DrawTargetHighlight(target);
|
||||
|
||||
// 绘制目标周围的场半径
|
||||
DrawHighlightFieldRadiusAroundTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:绘制彩色目标高亮
|
||||
private void GenDraw_DrawTargetHighlightWithColor(LocalTargetInfo target, Color color)
|
||||
{
|
||||
GenDraw.DrawTargetHighlight(target);
|
||||
}
|
||||
|
||||
// 重写OnGUI,显示自定义鼠标图标
|
||||
public override void OnGUI(LocalTargetInfo target)
|
||||
{
|
||||
// 使用自定义图标或默认攻击图标
|
||||
Texture2D icon;
|
||||
if (!target.IsValid)
|
||||
{
|
||||
icon = TexCommand.CannotShoot;
|
||||
}
|
||||
else if (target.Cell == caster.Position)
|
||||
{
|
||||
// 可以使用自定义图标,这里使用攻击图标
|
||||
icon = TexCommand.Attack;
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = (UIIcon != BaseContent.BadTex) ? UIIcon : TexCommand.Attack;
|
||||
}
|
||||
|
||||
GenUI.DrawMouseAttachment(icon);
|
||||
}
|
||||
|
||||
// 重写ValidateTarget,允许向自己脚下射击
|
||||
public override bool ValidateTarget(LocalTargetInfo target, bool showMessages = true)
|
||||
{
|
||||
// 如果目标是脚下,总是允许
|
||||
if (target.IsValid && target.Cell == caster.Position)
|
||||
return true;
|
||||
|
||||
// 否则使用基类验证逻辑
|
||||
return base.ValidateTarget(target, showMessages);
|
||||
}
|
||||
|
||||
// 重写Available,确保有抛射体,并允许在近战状态下使用
|
||||
public override bool Available()
|
||||
{
|
||||
// 首先调用基类检查
|
||||
if (!base.Available())
|
||||
return false;
|
||||
|
||||
// 检查是否有抛射体
|
||||
if (Projectile == null)
|
||||
return false;
|
||||
|
||||
// 特殊处理:允许在近战威胁下使用
|
||||
if (CasterIsPawn && CasterPawn.mindState != null && CasterPawn.mindState.MeleeThreatStillThreat)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 重写OrderForceTarget,允许在近战距离内强制使用
|
||||
public override void OrderForceTarget(LocalTargetInfo target)
|
||||
{
|
||||
// 如果是近战攻击,使用近战逻辑
|
||||
if (verbProps.IsMeleeAttack)
|
||||
{
|
||||
Job job = JobMaker.MakeJob(JobDefOf.AttackMelee, target);
|
||||
job.playerForced = true;
|
||||
if (target.Thing is Pawn pawn)
|
||||
{
|
||||
job.killIncappedTarget = pawn.Downed;
|
||||
}
|
||||
CasterPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否在近战范围内,但允许向脚下射击
|
||||
float minRange = verbProps.EffectiveMinRange(target, CasterPawn);
|
||||
if (CasterIsPawn &&
|
||||
(float)CasterPawn.Position.DistanceToSquared(target.Cell) < minRange * minRange &&
|
||||
CasterPawn.Position.AdjacentTo8WayOrInside(target.Cell))
|
||||
{
|
||||
// 如果是向脚下射击,允许
|
||||
if (target.IsValid && target.Cell == CasterPawn.Position)
|
||||
{
|
||||
// 允许向脚下射击
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message("MessageCantShootInMelee".Translate(), CasterPawn, MessageTypeDefOf.RejectInput, historical: false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建射击工作
|
||||
Job job2 = JobMaker.MakeJob(verbProps.ai_IsWeapon ? JobDefOf.AttackStatic : JobDefOf.UseVerbOnThing);
|
||||
job2.verbToUse = this;
|
||||
job2.targetA = target;
|
||||
job2.endIfCantShootInMelee = false; // 设置为false,允许在近战中射击
|
||||
CasterPawn.jobs.TryTakeOrderedJob(job2, JobTag.Misc);
|
||||
}
|
||||
|
||||
// 重写TryStartCastOn,允许在近战状态下开始射击
|
||||
public override bool TryStartCastOn(LocalTargetInfo castTarg, LocalTargetInfo destTarg, bool surpriseAttack = false, bool canHitNonTargetPawns = true, bool preventFriendlyFire = false, bool nonInterruptingSelfCast = false)
|
||||
{
|
||||
// 调用基类方法,但设置一个标志表示这是向脚下射击
|
||||
bool isShootingUnderfoot = castTarg.IsValid && castTarg.Cell == caster.Position;
|
||||
|
||||
// 如果是向脚下射击,临时修改一些属性以允许近战射击
|
||||
if (isShootingUnderfoot && CasterIsPawn && CasterPawn.mindState != null && CasterPawn.mindState.MeleeThreatStillThreat)
|
||||
{
|
||||
// 临时忽略近战威胁检查
|
||||
bool originalAIProjectileLaunchingIgnoresMeleeThreats = verbProps.ai_ProjectileLaunchingIgnoresMeleeThreats;
|
||||
verbProps.ai_ProjectileLaunchingIgnoresMeleeThreats = true;
|
||||
|
||||
try
|
||||
{
|
||||
return base.TryStartCastOn(castTarg, destTarg, surpriseAttack, canHitNonTargetPawns, preventFriendlyFire, nonInterruptingSelfCast);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 恢复原始值
|
||||
verbProps.ai_ProjectileLaunchingIgnoresMeleeThreats = originalAIProjectileLaunchingIgnoresMeleeThreats;
|
||||
}
|
||||
}
|
||||
|
||||
return base.TryStartCastOn(castTarg, destTarg, surpriseAttack, canHitNonTargetPawns, preventFriendlyFire, nonInterruptingSelfCast);
|
||||
}
|
||||
|
||||
// 添加一个方法,检查是否在近战状态下
|
||||
public bool IsInMeleeCombat()
|
||||
{
|
||||
if (!CasterIsPawn)
|
||||
return false;
|
||||
|
||||
return CasterPawn.mindState?.MeleeThreatStillThreat == true;
|
||||
}
|
||||
|
||||
// 重写BurstingTick,在近战状态下也继续射击
|
||||
public override void BurstingTick()
|
||||
{
|
||||
base.BurstingTick();
|
||||
|
||||
// 在近战状态下也继续射击逻辑
|
||||
if (IsInMeleeCombat() && state == VerbState.Bursting)
|
||||
{
|
||||
// 可以在这里添加近战状态下的特殊效果
|
||||
}
|
||||
}
|
||||
|
||||
// 添加自定义属性,用于控制是否总是向脚下发射
|
||||
private bool alwaysShootUnderfoot = true;
|
||||
|
||||
public bool AlwaysShootUnderfoot
|
||||
{
|
||||
get => alwaysShootUnderfoot;
|
||||
set => alwaysShootUnderfoot = value;
|
||||
}
|
||||
|
||||
// 添加一个方法,允许临时关闭向脚下射击
|
||||
public void SetShootUnderfoot(bool shootUnderfoot)
|
||||
{
|
||||
alwaysShootUnderfoot = shootUnderfoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user